当绑定值更改时,突出显示WPF DataGrid中的单元格

Pau*_*ott 13 wpf datagrid mvvm storyboard

我有一个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>
Run Code Online (Sandbox Code Playgroud)

然后将其应用于我想要突出显示值的列

<DataGridTextColumn Header="Status" 
    Binding="{Binding Path=Status, NotifyOnTargetUpdated=True}" 
    CellStyle="{StaticResource ChangedCellStyle}" />
Run Code Online (Sandbox Code Playgroud)

如果数据库中状态字段的值发生更改,则单元格将以黄色突出显示,就像我想要的那样.但是,有一些问题.

首先,当最初加载数据网格时,整个列以黄色突出显示.这是有道理的,因为所有的值都是第一次加载,因此您可能希望触发TargetUpdated.我确信有一些方法可以阻止这一点,但这是一个相对较小的一点.

真正的问题是如果以任何方式对网格进行排序或过滤,整个列将以黄色突出显示.我想我不明白为什么排序会导致TargetUpdated触发,因为数据没有改变,只是它的显示方式.

所以我的问题是(1)如何在初始加载和排序/过滤时停止这种行为,以及(2)我是否在正确的轨道上,这是一个很好的方法吗?我应该提到这是MVVM.

Kyl*_*Ren 0

因为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>
Run Code Online (Sandbox Code Playgroud)

隐藏代码(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);
    }
Run Code Online (Sandbox Code Playgroud)

模型类: SourceUpdating属性设置为 true(将绑定设置为TargetUpdate通过 ainDataTrigger正在进行任何通知并且更新通知到 后,设置为 false(然后将绑定重置为不通过 a) 。MyClassupdateProperty()UISourceUpdatingTargetUpdateDataTrigger

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;                
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

输出:

两个同时更新/按钮被单击一次:

更新1

许多同时更新/按钮被点击多次:

更新2

因此,更新后,当发生排序或过滤时,绑定知道它不必调用该TargetUpdated 事件。仅当源集合更新正在进行时,绑定才会重置以调用事件TargetUpdated。最初的着色问题也可以通过此方法得到解决。

然而,由于编辑器的逻辑仍然存在一些问题,因此TextBox逻辑基于更复杂的数据类型和 UI 逻辑,对于初始绑定重置,代码也将变得更加复杂,整行将被动画化,就像TargetUpdated为一行的所有单元格引发的一样。