如何根据DataGrid中数据的变化及时改变背景颜色?

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

我在 DataGrid 中有它。目前,如果数据大于 150,我将行的背景颜色更改为红色,如果数据小于 150,则更改为绿色。我想做的是如果背景颜色改变后 5 秒内数据没有改变,它应该再次变成无色。我不确定如何实施。你能帮忙解决这个问题吗?

我的转换器

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>

编辑: 我尝试了以下方法。我接近我想要的,但它只发生一次,因为它有一个 Onloaded 事件。我找不到我应该使用哪个 activity。有这样的吗?

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

在 MVVM 模式中,它可能看起来像这样:

型号:

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

视图模型:

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

转换器:

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>

与使用故事板相同:

    <DataGrid ItemsSource="{Binding Lines}"
              AutoGenerateColumns="False">
        <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}">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Setter Property="Background" Value="Green"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding ID, Converter={StaticResource IntToBooleanConverter}}" Value="True">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <StaticResource ResourceKey="KeyFramesAnimation"/>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

转换器:

public class IntToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is int valInt ? valInt > 150 : Binding.DoNothing;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

我修复了解决方案,现在它每次都有效。但是我不得不添加一个额外的 class 并重新定义事件。不再需要转换器。

帮手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>

事件处理程序:

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