【问题标题】:Usage of TextSearch.Text in DataTemplate在 DataTemplate 中使用 TextSearch.Text
【发布时间】:2012-04-26 06:48:05
【问题描述】:

我有一个非常简单的例子:带有单个表单的 WPF 表单应用程序,其中包含带有数据的字典:

Dim dict As New Collections.Generic.Dictionary(Of String, String)

Private Sub MainWindow_Loaded() Handles Me.Loaded
    dict.Add("One", "1")
    dict.Add("Two", "2")
    dict.Add("Three", "3")

    lst1.ItemsSource = dict
End Sub

在表单上,​​我有一个 ListBox(名为“lst1”),它使用“dict”作为项目源:

<ListBox x:Name="lst1">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Label Content="{Binding Value}" 
                   TextSearch.Text="{Binding Path=Key, Mode=OneWay}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

我还有一个未绑定的 ListBox,手动预填充值:

<ListBox>
    <Label TextSearch.Text="One" Content="1" />
    <Label TextSearch.Text="Two" Content="2" />
    <Label TextSearch.Text="Three" Content="3" />
</ListBox>

所以当我启动应用程序时,它看起来像这样:

问题:

如果我尝试通过键入“一”、“二”或“三”来使用键盘导航项目,我只会在非绑定列表框中成功。绑定列表框失败。

一些备注: 1.)如果我在绑定列表框中按“[”,焦点以循环方式从一个项目变为另一个项目:它从 1 变为 2,从 2 变为 3,从 3 变为 1,从 1 再次变为 2,等等。 2.) 我已经用 Snoop 检查了应用程序。我发现绑定列表框和非绑定列表框之间的一个区别。两个列表框都在标签控件(在 ItemsPresenter 内)上设置了 TextSearch.Text 属性。但对于非绑定情况:TextSearch.Text 属性的“值源”是“本地”。对于绑定案例:“值源”是“ParentTemplate”。

附注(和 N.B.) 我知道我可以在列表框上使用 TextSearch.TextPath,但这不是我需要的 :) 此外,为 ListViewItem 设置 TextSearch.Text 属性(通过使用样式)也无济于事。

【问题讨论】:

标签: .net wpf binding datatemplate text-search


【解决方案1】:

让我首先解释一下 TextSearch 如何与 ItemsControl 一起工作:

TextSearch 的实现会枚举ItemsSource 属性的实际数据项并查看那些直接 以读取Text 依赖属性。当您像在示例中那样将ListBoxItems 放入其中时,它会起作用,因为实际项目是ListBoxItem 实例,其中Text 依赖属性“附加”到它们。一旦你绑定到你的Dictionary&lt;&gt;,它现在直接查看KeyValuePair&lt;&gt; 实例,这些实例不是DependencyObjects,因此不能/没有TextSearch.Text 属性。这也是为什么通过ItemContainerStyleListBoxItem 上设置TextSearch.Text 属性无效的原因:ItemContainerStyle 描述了您的数据在可视化树中的外观,但TextSearch 引擎只考虑原始数据数据源。无论您如何在 UI 中设置该数据的样式并不重要,这就是为什么修改 DataTemplateTextSearch 永远不会做任何事情的原因。

另一种方法是创建一个从DependencyObject 继承的视图模型类,您可以在其中根据您希望可搜索的值设置TextSearch.Text 附加属性。下面是一些示例代码,展示了它是如何工作的:

private sealed class MyListBoxItem : DependencyObject
{
    public static readonly DependencyProperty KeyProperty = DependencyProperty.Register("Key", typeof(string), typeof(MyListBoxItem), new FrameworkPropertyMetadata(string.Empty));
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(MyListBoxItem), new FrameworkPropertyMetadata(string.Empty));

    public string Key
    {
        get
        {
            return (string)GetValue(KeyProperty);
        }
        set
        {
            SetValue(KeyProperty, value);
            SetValue(TextSearch.TextProperty, value);
        }
    }

    public string Value
    {
        get
        {
            return (string)GetValue(ValueProperty);
        }
        set
        {
            SetValue(ValueProperty, value);
        }
    }
}

// Assign a list of these as the list box's ItemsSource
this.listBox.ItemsSource = new List<MyListBoxItem>
{
    new MyListBoxItem { Key = "One", Value = "1" },
    new MyListBoxItem { Key = "Two", Value = "2" },
    new MyListBoxItem { Key = "Three", Value = "3" }
};

ListBox 定义如下所示:

<ListBox Name="listBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Value}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

任何其他选择都需要您使用TextSearch.TextPath,但您似乎已经死定了。如果您接受修改 DataTemplate 将永远无法工作,我建议的解决方案是简单地创建一个 POCO 视图模型,其中包含您要用于搜索的属性并将其指定为 TextSearch.TextPath。这是完成您正在做的事情的最轻巧、最简单的方法。

【讨论】:

  • 感谢您的回复,德鲁。但是 TextSearch.TextPath 不是一个选项(正如我在问题中已经说过的那样)。
  • 这个例子是从更大的项目中提取的。在项目中,我将对象绑定到列表。每个项目的文本是通过值转换器(它使用对象实例的几个属性)生成的。所以我想使用相同的转换器为 TextSearch.Text 提供值。
  • 你明白我对运行时如何与 TextSearch.Text 一起工作的解释吧?您根本无法使用 TextSearch.Text 属性来做到这一点。否决票似乎表明您拒绝接受它。您不必接受我的回答,但反对票相当苛刻。我有办法做到这一点,但你必须放弃转换器的想法。如果您愿意接受 TextSearch.Text 无法正常工作,我可以与您分享它,并且将对我在帮助您方面所付出的时间和努力表示感谢,至少消除您的反对票。
  • 我真的很欣赏你的回答德鲁。赖特现在我正在对你写的关于直接查看 KeyValuePair 的内容进行一些调查(我有检查所有内容的习惯;))。您的陈述似乎合乎逻辑,但另一方面:键值对应该包装在项目容器中,不是吗?并且项目容器是可以使用附加属性的依赖对象。
  • 我已经测试了你的理论,但不幸的是它没有帮助。我创建了一个继承自 DependencyObject 的类。添加了单个属性:文本(作为字符串)。很简单的课。创建了一个对象集合。将集合绑定到 ListBox 控件。将 TextSearch.Text 设置为“{Binding Text}”。但是什么也没发生。不过,如果我使用 TextSearch.TextPath - 它可以工作。但正如我所说,这不是一个选择。如果你能证明我错了,我很乐意删除我的反对票。请不要难过。我们在这里寻找真相。
【解决方案2】:

如果未设置 TextSearch.TextTextSearch.TextPath,则可能的解决方案是使用 TextSearch 的后备行为,即在 ListBoxItem 的数据项上使用 .ToString()。

例如,这将允许您在不指定 TextSearch.Text 或 .TextPath 的情况下进行搜索。

<Page.DataContext>
    <Samples:TextSearchViewModel/>
</Page.DataContext>

<Grid>
    <ListBox ItemsSource="{Binding Items}" 
             IsTextSearchCaseSensitive="False" 
             IsTextSearchEnabled="True">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding Value}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

public class TextSearchItem
{
    public int Value { get; set; }
    public string SearchText { get; set; }

    public override string ToString()
    {
        return SearchText;
    }
}

public class TextSearchViewModel
{
    public TextSearchViewModel()
    {
        Items = new List<TextSearchItem>
                    {
                        new TextSearchItem{ Value = 1, SearchText = "One"},
                        new TextSearchItem{ Value = 2, SearchText = "Two"},
                        new TextSearchItem{ Value = 3, SearchText = "Three"},
                        new TextSearchItem{ Value = 4, SearchText = "Four"},
                    };
    }

    public IEnumerable<TextSearchItem> Items { get; set; }
}

【讨论】:

  • 非常好!我喜欢有创意的答案。
  • 太棒了!开箱即用思维的好例子。我不知道从这个角度有一个可用的解决方案。我可以确认它有效。
猜你喜欢
  • 2010-11-09
  • 1970-01-01
  • 2013-05-16
  • 2010-12-15
  • 1970-01-01
  • 2013-02-25
  • 2015-09-27
  • 1970-01-01
相关资源
最近更新 更多