由于TargetUpdated
事件仅基于UI更新。无论更新如何进行,所有的DataGridCells
在排序时都保持不变,只有其中的数据根据排序结果发生了更改,因此会触发TargetUpdated
事件。因此,我们必须依赖于WPF应用程序的数据层来实现这一点。为了实现这一点,我根据一个变量重置了DataGridCell
的绑定,以跟踪是否在数据层进行更新。
XAML:
<Window.Resources>
<Style x:Key="ChangedCellStyle" TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="00:00:04" Storyboard.TargetName="myTxt"
Storyboard.TargetProperty="(DataGridCell.Background).(SolidColorBrush.Color)"
From="Red" To="Transparent" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent"
Name="myTxt" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=DataContext.SourceUpdating}" Value="True">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Content.Text,NotifyOnSourceUpdated=True,NotifyOnTargetUpdated=True}" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=DataContext.SourceUpdating}" Value="False">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Content.Text}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical">
<DataGrid ItemsSource="{Binding list}" CellStyle="{StaticResource ChangedCellStyle}" AutoGenerateColumns="False"
Name="myGrid" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="ID" Binding="{Binding Id}" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Change Values" Click="Button_Click" />
</StackPanel>
窗口的Code Behind(DataContext对象):
public MainWindow()
{
list = new ObservableCollection<MyClass>();
list.Add(new MyClass() { Id = 1, Name = "aa" });
list.Add(new MyClass() { Id = 2, Name = "bb" });
list.Add(new MyClass() { Id = 3, Name = "cc" });
list.Add(new MyClass() { Id = 4, Name = "dd" });
list.Add(new MyClass() { Id = 5, Name = "ee" });
list.Add(new MyClass() { Id = 6, Name = "ff" });
InitializeComponent();
}
private ObservableCollection<MyClass> _list;
public ObservableCollection<MyClass> list
{
get{ return _list; }
set{
_list = value;
updateProperty("list");
}
}
Random r = new Random(0);
private void Button_Click(object sender, RoutedEventArgs e)
{
int id = (int)r.Next(6);
list[id].Id += 1;
int name = (int)r.Next(6);
list[name].Name = "update " + r.Next(20000);
}
模型类:当MyClass
在updateProperty()
方法中发出任何通知并且更新已通知到UI
后,将SourceUpdating
属性设置为true(这将通过DataTrigger
将绑定设置为通知TargetUpdate
),然后将SourceUpdating
设置为false(这将重置绑定以不通过DataTrigger
通知TargetUpdate
)。
public class MyClass : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set {
name = value;updateProperty("Name");
}
}
private int id;
public int Id
{
get { return id; }
set
{
id = value;updateProperty("Id");
}
}
private bool sourceUpdating;
public bool SourceUpdating
{
get { return sourceUpdating; }
set
{
sourceUpdating = value;updateProperty("SourceUpdating");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void updateProperty(string name)
{
if (name == "SourceUpdating")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
else
{
SourceUpdating = true;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
SourceUpdating = false;
}
}
}
输出:
两个同时更新/按钮被点击一次:
![update1](https://istack.dev59.com/iisMx.webp)
许多同时更新/按钮被点击多次:
![update2](https://istack.dev59.com/F2Hqy.webp)
更新后,当进行排序或筛选时,绑定知道它不必调用
TargetUpdated
事件。只有在源集合的更新正在进行时,绑定才会重置以调用
TargetUpdated
事件。同时,这也解决了初始着色问题。
然而,由于逻辑仍然存在一些缺陷,对于编辑器
TextBox
,逻辑基于更复杂的数据类型和UI逻辑,代码将变得更加复杂,对于初始绑定重置,整个行都会被动画化,因为
TargetUpdated
被触发了一整行的所有单元格。