I have a very simple example: WPF form application with single form which contains a dictionary with data:
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
On form I have a ListBox (named "lst1"), which uses "dict" as items source:
<ListBox x:Name="lst1">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Value}"
TextSearch.Text="{Binding Path=Key, Mode=OneWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Also I have one non-bound ListBox, prefilled with values manually:
<ListBox>
<Label TextSearch.Text="One" Content="1" />
<Label TextSearch.Text="Two" Content="2" />
<Label TextSearch.Text="Three" Content="3" />
</ListBox>
So when I launch app, it looks like this:
If I try to navigate items with keyboard by typing "one", "two" or "three", I succeed only in non-bound list box. Bound list box fails.
Some remarks: 1.) If I press "[" while in bound list box, focus changes from item to item in cyclic manner: it goes from 1 to 2, from 2 to 3, from 3 to 1, from 1 again to 2 etc. 2.) I have checked app with Snoop. One difference I found between bound and non-bound list boxes. Both list boxes have TextSearch.Text property set on Label controls (inside ItemsPresenter). But for non-bound case: "value source" of TextSearch.Text property is "Local". For bound case: "value source" is "ParentTemplate".
P.S. (and N.B.) I know that I can use TextSearch.TextPath on the list box, but this is not what I need :) Also, setting TextSearch.Text property for ListViewItem (by using Style) does not help.
Let me start by explaining exactly how TextSearch works with the ItemsControl:
TextSearch's implementation enumerates the actual data items of the ItemsSource
property and looks at those directly to read the Text
dependency property. When you put ListBoxItem
s in it like you do in your example it works because the actual items are ListBoxItem
instances with the Text
dependency property "attached" to them. Once you bind to your Dictionary<>
it's now looking directly at KeyValuePair<>
instances which are not DependencyObject
s and thus can't/don't have the TextSearch.Text
property on them. This is also why setting the TextSearch.Text
property on the ListBoxItem
via the ItemContainerStyle
has no effect: the ItemContainerStyle
is describing how your data should look in the visual tree, but the TextSearch
engine only considers the raw data source. It does not matter how you've styled that data in the UI and that is why modifying a DataTemplate
will never do anything for TextSearch
.
One alternative is creating a view model class that inherits from DependencyObject
where you set the TextSearch.Text
attached property on that based on the value you want to be searchable. Here's some sample code that shows how this would work:
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 definition looks something like this:
<ListBox Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Any other alternative will require you to use TextSearch.TextPath
, but you seem dead set against that. If you come to accept that modifying the DataTemplate
will never work, the solution I would recommend is to simply create a POCO view-model that has the property on it that you want to use for searching and specify that to TextSearch.TextPath
. It is the lightest weight, non-hacky way to accomplish what you're doing.
A possible solution for you is to use TextSearch's fallback behaviour of using .ToString() on the data item of the ListBoxItem
if no TextSearch.Text
or TextSearch.TextPath
are set.
So for example, this will allow you to search without specifying TextSearch.Text or .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; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With