Suppose I have a basic control with a listbox and a text box, where the listbox is bound to a collection of objects and has a basic data template
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top">Book name</TextBlock>
<TextBox x:Name="bookNameTextBox" DockPanel.Dock="Top" />
<TextBlock DockPanel.Dock="Top">Authors</TextBlock>
<ListBox ItemsSource="{Binding Authors}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
public class Author : INotifyPropertyChanged
{
public string Name { get; set; }
public ObservableCollection<Book> Books { get; }
}
public class Book : INotifyPropertyChanged
{
public string Name { get; }
}
What I want to do is have the colours of the items in the listbox change depending on if that author has any books that match the supplied name, for example
Colour = author.Books.Any(b => b.Name.StartsWith(bookNameTextBox.Text)) ? Red : Black;
I initially thought that I could do this using a MultiBinding and a converter, however I couldn't work out how to get the binding to update when items were added to / removed from the books collection, or whent he name of a book changed.
How can I do this in such a way that the colour will update correctly in response to all of the various changes that could affect my logic? e.g.
- The name of any of the books changing
- Books being added and removed from the collection
- The text in the
bookNameTextBox
text box changing
My MultiBinding looked like this
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="Books" />
<Binding Path="Text" ElementName="bookNameTextBox" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
And my converter (which implemented IMultiValueConverter
) looked like this
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var text = (string)values.First(v => v is string);
var books = (IEnumerable<Book>)values.First(v => v is IEnumerable<Book>);
return books.Any(b => b.Name.StartsWith(text));
}
This worked however if I then modified any of the books, or added any books the text colour of the list item would not update until the binding was somehow refreshed.