绑定值更改时突出显示 WPF DataGrid 中的单元格 [英] Highlighting cells in WPF DataGrid when the bound value changes

查看:25
本文介绍了绑定值更改时突出显示 WPF DataGrid 中的单元格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 DataGrid,它的数据每 15 秒由后台进程刷新一次.如果任何数据发生变化,我想运行一个动画,以黄色突出显示具有更改值的单元格,然后淡入白色.我通过执行以下操作使其工作:

I have a DataGrid that has its data refreshed by a background process every 15 seconds. If any of the data changes, I want to run an animation that highlights the cell with the changed value in yellow and then fade back to white. I sort-of have it working by doing the following:

我在 Binding.TargetUpdated 上创建了一个带有事件触发器的样式

I created a style with event trigger on 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>

然后将其应用于我想在值更改时突出显示的列

And then applied it to the columns I wanted to highlight if a value changes

<DataGridTextColumn Header="Status" 
    Binding="{Binding Path=Status, NotifyOnTargetUpdated=True}" 
    CellStyle="{StaticResource ChangedCellStyle}" />

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

If the value for the status field in the database changes, the cell highlights in yellow just like I want. But, there are a few problems.

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

First, when the data grid is initially loaded, the entire column is highlighted in yellow. This makes sense, because all of the values are being loaded for the first time so you would expect TargetUpdated to fire. I'm sure there is some way I can stop this, but it's a relatively minor point.

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

The real problem is the entire column is highlighted in yellow if the grid is sorted or filtered in any way. I guess I don't understand why a sort would cause TargetUpdated to fire since the data didn't change, just the way it is displayed.

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

So my question is (1) how can I stop this behavior on initial load and sort/filter, and (2) am I on the right track and is this even a good way to do this? I should mention this is MVVM.

推荐答案

由于 TargetUpdated 确实只是基于 UI 更新的事件.更新发生的方式无关紧要.在对所有 DataGridCells 进行排序时,仅根据排序结果更改其中的数据,因此会引发 TargetUpdated.因此我们必须依赖 WPF 应用程序的数据层.为了实现这一点,我根据一个变量重置了 DataGridCell 的绑定,如果更新发生在数据层.

Since TargetUpdated is truly only UI update based event. It doesn't matter how update in happening. While sorting all the DataGridCells remain at their places only data is changed in them according to sorting result hence TargetUpdatedis raised. hence we have to be dependent on data layer of WPF app. To achieve this I've reset the Binding of DataGridCell based on a variable that kind of trace if update is happening at data layer.

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(设置绑定以通过DataTrigger) 当 updateProperty() 方法中的 MyClass 有任何通知正在进行时,更新后通知 UI, SourceUpdating 设置为 false(然后重置绑定以不通过 DataTrigger 通知 TargetUpdate).

Model Class: SourceUpdating property is set to true(which set the binding to notify TargetUpdate via a DataTrigger) when any notification is in progress for MyClass in updateProperty() method and after update is notified to UI, SourceUpdating is set to false(which then reset the binding to not notify TargetUpdate via a DataTrigger).

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

}

输出:

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

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

SO 更新后,当排序或过滤发生时,绑定知道它不必调用 TargetUpdated事件.仅当源集合的更新正在进行时绑定被重置以调用 TargetUpdated 事件.初始着色问题也由此得到处理.

SO after update, when sorting or filtering is happening the bindings know that it doesn't have to invoke the TargetUpdated event. Only when the update of source collection is in progress the binding is reset to invoke the TargetUpdated event. Also initial coloring problem is also get handled by this.

然而,对于编辑器 TextBox 逻辑仍然有某种形式,该逻辑基于更复杂的数据类型和 UI 逻辑,对于初始绑定重置整行,代码也将变得更加复杂为一行的所有单元格引发 TargetUpdated 动画.

However as the logic still has some sort comings as for editor TextBox the logic is based on with more complexity of data types and UI logic the code will become more complex also for initial binding reset whole row is animated as TargetUpdated is raised for all cells of a row.

这篇关于绑定值更改时突出显示 WPF DataGrid 中的单元格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆