How can I change the property of an Element based on the property of an INotifyPropertyChanged object in an ObservableCollection?
I've got an ObservableCollection<User>
full of User objects which implement INotifyPropertyChanged
. The collection is set as the DataContext
of my Window, which contains a ListBox
(whose ItemsSource
is also set to the same collection), a number of TextBox
es, and a save Button
, standard CRUD setup.
I want to change the background of the save Button (and the background of the row in the ListBox which corresponds to the "current item") if one of the properties of the User objects changes. Should I be looking at styles and triggers?
I have the following Style applied to my save Button, and the User objects have a public bool IsDirty
property.
<Style x:Key="PropertyChangedStyle" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Source=???, Path=IsDirty}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
<Button ... Style="{StaticResource PropertyChangedStyle}">
I think I'm on the right track, but I don't understand how to point the binding to "the current item in an observable list which is set as the datacontext", where "current item" in this case is described by CollectionViewSource.GetDefaultView(ListOfUsers).CurrentItem
(where ListOfUsers
is my ObservableCollection<User>
开发者_运维技巧).
The DataContext
of each of the items in your ListBox will be automatically bound to your User
instances, so it is not necessary to set the source in your binding. You can bind directly from the style of your ListBoxItem
s to properties on your User
instances.
You can achieve it like this:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ASD_Answer011.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Property1}"/>
<CheckBox IsChecked="{Binding Property2}"/>
</StackPanel>
</DataTemplate>
<Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsDirty}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource SampleDataSource}}">
<ListBox ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" ItemContainerStyle="{DynamicResource ListBoxItemStyle1}"/>
</Grid>
</Window>
This is how it looks when the application is running:
WPF supports the idea of a "current item" in a collection, and will track the current item for you. You can write a binding path that refers to a collection's current item.
See the "Current Item Pointers" section on the Data Binding Overview page on MSDN.
I'm thinking that, if your ListBox's ItemsSource is bound to (for example) {Binding ListOfUsers}
, then your button could use {Binding ListOfUsers/IsDirty}
.
I haven't used this much, but I think you may have to set your ListBox's IsSynchronizedWithCurrentItem property to True to make this work.
精彩评论