Highlighting cells in WPF DataGrid when the bound value changes

前端 未结 3 835
自闭症患者
自闭症患者 2021-01-01 21:42

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

相关标签:
3条回答
  • 2021-01-01 22:01

    I suggest to use OnPropertyChanged for every props in your viewmodel and update related UIElement (start animation or whatever), so your problem will solved (on load, sort, filter,...) and also users can saw which cell changed!

    0 讨论(0)
  • 2021-01-01 22:18

    My ideas for point (1) would be to handle this in the code. One way would be to handle the TargetUpdated event for the DataGridTextColumn and do an extra check on the old value vs. the new value, and apply the style only if the values are different, and perhaps another way would be to create and remove the binding programmatically based on different events in your code (like initial load, refresh, etc).

    0 讨论(0)
  • 2021-01-01 22:20

    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>
    

    Code Behind(DataContext object of Window):

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

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

    Outputs:

    Two simultaneous Updates/ Button is clicked once :

    Many simultaneous Updates/ Button is clicked many times :

    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.

    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.

    0 讨论(0)
提交回复
热议问题