7

我有一个非常简单的示例:具有单个表单的 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>
    <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”。

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

4

2 回答 2

12

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

ItemsSourceTextSearch 的实现会枚举属性的实际数据项并直接查看这些数据项以读取Text依赖项属性。当您ListBoxItem像在示例中那样将 s 放入其中时,它会起作用,因为实际项目是具有“附加”到它们ListBoxItem的依赖属性的实例。Text一旦你绑定到你的Dictionary<>它现在直接查看KeyValuePair<>不是 s 的实例,DependencyObject因此不能/没有它们的TextSearch.Text属性。这也是为什么在via上设置TextSearch.Text属性没有效果的原因:它描述了您的数据在可视化树中的外观,但是ListBoxItemItemContainerStyleItemContainerStyleTextSearch引擎只考虑原始数据源。在 UI 中如何设置数据的样式并不重要,这就是为什么修改 aDataTemplate永远不会对TextSearch.

一种替代方法是创建一个视图模型类,该类继承自您根据要搜索的值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. 这是完成您正在做的事情的最轻巧、最简单的方法。

于 2012-04-16T04:47:20.027 回答
6

ListBoxItem一个可能的解决方案是使用 TextSearch 的后备行为,即在if no TextSearch.Textor TextSearch.TextPathare set的数据项上使用 .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; }
}
于 2012-04-18T11:33:20.130 回答