我有一个DataGrid,它的数据每15秒由后台进程刷新一次。如果任何数据发生变化,我想运行一个动画,突出显示黄色更改值的单元格,然后淡出为白色。我通过以下方式让它工作:
我在Binding.TargetUpdated
上创建了一个带有事件触发器的样式<Style x:Key="ChangedCellStyle" TargetType="DataGridCell">
<Style.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="00:00:15"
Storyboard.TargetProperty=
"(DataGridCell.Background).(SolidColorBrush.Color)"
From="Yellow" To="Transparent" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
然后将其应用于我希望在值更改时突出显示的列
<DataGridTextColumn Header="Status"
Binding="{Binding Path=Status, NotifyOnTargetUpdated=True}"
CellStyle="{StaticResource ChangedCellStyle}" />
如果数据库中状态字段的值发生更改,则单元格将以黄色突出显示,就像我想要的那样。但是,有一些问题。
首先,最初加载数据网格时,整个列以黄色突出显示。这是有道理的,因为所有的值都是第一次加载,因此您可能希望触发TargetUpdated。我确信有一些方法可以阻止这一点,但这是一个相对较小的一点。
如果网格以任何方式排序或过滤,真正的问题是整个列以黄色突出显示。我想我不明白为什么排序会导致TargetUpdated触发,因为数据没有改变,只是它的显示方式。
所以我的问题是(1)如何在初始加载和排序/过滤时停止此行为,以及(2)我是否在正确的轨道上,这是一个很好的方法吗?我应该提到这是MVVM。
答案 0 :(得分:0)
我对点(1)的想法是在代码中处理这个问题。一种方法是处理DataGridTextColumn的TargetUpdated事件,并对旧值和新值进行额外检查,并仅在值不同时应用样式,或许另一种方法是创建和删除绑定以编程方式基于代码中的不同事件(如初始加载,刷新等)。
答案 1 :(得分:0)
我建议对viewmodel中的每个道具使用OnPropertyChanged并更新相关的UIElement(开始动画或其他),这样你的问题就会得到解决(加载,排序,过滤......),用户也可以看到哪个单元格改变了!
答案 2 :(得分:0)
由于TargetUpdated
确实只是基于UI更新的事件。如何更新发生并不重要。在对所有DataGridCells
进行排序时,只会根据排序结果更改其中的数据,因此会引发TargetUpdated
。因此我们必须依赖于WPF app的数据层。为了达到这个目的,如果在数据层发生更新,我会根据变量来重置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>
代码隐藏(Window的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);
}
模型类: SourceUpdating
属性设置为true(通过TargetUpdate
将绑定设置为通知DataTrigger
)当MyClass
方法中updateProperty()
的任何通知正在进行中,并且在向UI
通知更新后,SourceUpdating
设置为false(然后将绑定重置为不通知{ {1}}通过TargetUpdate
)。
DataTrigger
<强> 输出: 强>
点击两次同步更新/按钮:
多次点击多个同步更新/按钮:
SO更新后,当排序或过滤发生时,绑定知道它不必调用
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"); } } //the vaiable must set to ture when update in this calss is ion progress 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; } } }
事件。只有当源集合的更新正在进行时 重置绑定以调用TargetUpdated
事件。此外,初始着色问题也得到了处理。
然而,由于逻辑仍然存在编辑器TargetUpdated
的某些排序,逻辑基于更复杂的数据类型和UI逻辑,代码将变得更加复杂,对于初始绑定重置整行被动画为对于一行的所有单元格都会引发TextBox
。