'How to change the background color timely depending on the data change in the DataGrid?

I have it in a DataGrid. Currently I am changing the background color of the row to be Red if the data is greater than 150 then green if less. What I want to do is if the data does not change within 5 seconds after the background color changes, it should turn colorless again. I wasn't sure how to implement this. Can you please help with this?

My Converter

class ChangedDataConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var val = (int)value;
        return new SolidColorBrush(val > 150 ? Colors.Red : Colors.Green);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

}

xaml

<Syncfusion:GridTextColumn DisplayBinding="{Binding ID, Converter={StaticResource IntToHexadecimalConverter}}">
                            <Syncfusion:GridTextColumn.CellStyle>
                                <Style TargetType="Syncfusion:GridCell">
                                    <Setter Property="Background" Value="{Binding ID, Converter={StaticResource ChangedDataConverter}}"/>
                                </Style>
                            </Syncfusion:GridTextColumn.CellStyle>
                        </Syncfusion:GridTextColumn>

Edit: I tried the following way. I'm close to what I want, but it only happens once because it has an Onloaded event. I couldn't find which activity I should use. Is there anything like that ?

<Syncfusion:GridTextColumn DisplayBinding="{Binding ID, Converter={StaticResource ByteToHexadecimalConverter}}" Width="100">
                            <Syncfusion:GridTextColumn.CellStyle>
                                <Style TargetType="Syncfusion:GridCell">
                                    <Setter Property = "Background" Value="{Binding ID,Converter={StaticResource ChangedDataConverter}}"/>
                                    <Style.Triggers>
                                        <EventTrigger RoutedEvent="Loaded">
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <ColorAnimation Storyboard.TargetProperty="(DataGridRow.Background).(SolidColorBrush.Color)" 
                                                                    Duration="00:00:10" 
                                                                    To="Transparent"/>
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </EventTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Syncfusion:GridTextColumn.CellStyle>
                        </Syncfusion:GridTextColumn>


Solution 1:[1]

In the MVVM pattern, it might look like this:

Model:

public class LineModel : INotifyPropertyChanged
{
    private int iD;
    public int ID
    {
        get => iD;
        set
        {
            iD = value;
            OnPropertyChanged("ID");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

ViewModel:

public class ViewModel : INotifyPropertyChanged
{
    public Timer Tmr;
    private bool timeOut;
    public bool TimeOut
    {
        get => timeOut;
        set
        {
            timeOut = value;
            OnPropertyChanged();
        }
    }
    public ObservableCollection<LineModel> Lines { get; set; }
    private void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        TimeOut = false;
    }
    void LineModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        TimeOut = Lines != null && e.PropertyName == "ID";
        Tmr.Enabled = true;
    }
    public ViewModel()
    {
        Tmr = new Timer(5000);
        Tmr.Elapsed += OnTimedEvent;
        Tmr.AutoReset = false;
        var line = new LineModel() { ID = 10 };
        line.PropertyChanged += LineModel_PropertyChanged;
        Lines = new ObservableCollection<LineModel>() { line };
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

Converter:

class ChangedDataConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] is int valInt && values[1] is bool valTimeout)
            return new SolidColorBrush(valTimeout && valInt > 150 ? Colors.Red : Colors.Green);
        else return Binding.DoNothing;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

XAML:

        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding ID}" Width="300">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Setter Property="Background">
                            <Setter.Value>
                                <MultiBinding Converter="{StaticResource ChangedDataConverter}">
                                    <Binding Path="ID"/>
                                    <Binding RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}" Path="DataContext.TimeOut"/>
                                </MultiBinding>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>

Solution 2:[2]

I fixed the solution, now it works every time. But I had to add an additional class and redefine the event. The converter is no longer needed.

The helper class:

public static class EventMonitor
{
    public static readonly RoutedEvent CustomEvent =
        EventManager.RegisterRoutedEvent("Greater150", RoutingStrategy.Bubble,
        typeof(RoutedEventHandler), typeof(EventMonitor));
    internal static RoutedEventArgs RaiseAlarmEvent(DependencyObject target)
    {
        if (target is null) return null;
        RoutedEventArgs args = new RoutedEventArgs { RoutedEvent = CustomEvent };
        if (target is UIElement) (target as UIElement).RaiseEvent(args);
        else if (target is ContentElement) (target as ContentElement).RaiseEvent(args);
        return args;
    }
    public static DataGridCell GetDataGridCell(DataGridCellInfo cellInfo)
    {
        var cellContent = cellInfo.Column.GetCellContent(cellInfo.Item);
        if (cellContent != null)
            return (DataGridCell)cellContent.Parent;

        return null;
    }
}

XAML:

    <DataGrid HorizontalAlignment="Left"
              Height="303" Margin="30,78,0,0"
              Width="300"
              VerticalAlignment="Top"
              ItemsSource="{Binding Lines}"
              AutoGenerateColumns="False" CellEditEnding="DataGrid_CellEditEnding">
        <DataGrid.Resources>
            <ColorAnimationUsingKeyFrames x:Key="KeyFramesAnimation"
                    Storyboard.TargetProperty="(DataGridCell.Background).(SolidColorBrush.Color)">
                <LinearColorKeyFrame
                    KeyTime="0:0:0.3"
                    Value="Red"/>
                <LinearColorKeyFrame
                    KeyTime="0:0:4.7"
                    Value="Red"/>
                <LinearColorKeyFrame
                    KeyTime="0:0:5"
                    Value="Green"/>
            </ColorAnimationUsingKeyFrames>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding ID}" Width="290">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Setter Property="Background" Value="Green"/>
                        <Style.Triggers>
                            <EventTrigger RoutedEvent="local:EventMonitor.Greater150">
                                <BeginStoryboard>
                                    <Storyboard>
                                        <StaticResource ResourceKey="KeyFramesAnimation"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Style.Triggers>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

Event Handler:

    private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
    {
        if (e.EditingElement is TextBox textBox && double.TryParse(textBox.Text, out double result) && result > 150)
        {
            EventMonitor.RaiseAlarmEvent(EventMonitor.GetDataGridCell((sender as DataGrid).SelectedCells[0]));
        }
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Alexander Shapovalov
Solution 2 Alexander Shapovalov