You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF DataTrigger仅触发一次需求问题:DataGrid行动画重复触发解决方案咨询

问题分析

你遇到的问题根源在于WPF DataGrid的UI虚拟化机制:当行滚动出可视区域后,DataGrid会回收这些行容器(DataGridRow);当它们再次回到视野中时,会重新绑定数据上下文。此时虽然你的IsSaved属性没有变化,但DataTrigger会因为行重新加载、IsSaved=true的条件再次满足,而触发EnterActions,导致动画重复播放。

解决方案

这里提供几种可行的方案,你可以根据项目情况选择:

方案1:在ViewModel中添加动画已播放标记(MVVM友好)

在数据项的ViewModel里新增一个IsAnimationPlayed属性,用来记录动画是否已执行。修改DataTrigger为多条件触发,仅当IsSaved=true且IsAnimationPlayed=false时播放动画,同时在动画完成后标记IsAnimationPlayed=true。

修改后的ViewModel代码

private bool _isSaved;
public bool IsSaved
{
    get { return _isSaved; }
    set
    {
        if (value != _isSaved)
        {
            _isSaved = value;
            OnPropertyChanged();
        }
    }
}

private bool _isAnimationPlayed;
public bool IsAnimationPlayed
{
    get { return _isAnimationPlayed; }
    set
    {
        if (value != _isAnimationPlayed)
        {
            _isAnimationPlayed = value;
            OnPropertyChanged();
        }
    }
}

修改后的XAML代码

给Storyboard添加完成事件,并改用MultiDataTrigger:

<DataGrid.Resources>
    <Storyboard x:Key="AnimateYellowToTransparent" Completed="AnimateYellowToTransparent_Completed">
        <ColorAnimation Storyboard.TargetProperty="Background.(SolidColorBrush.Color)" 
                        From="#FFF4F496" To="Transparent"
                        Duration="0:0:1" RepeatBehavior="1x" />
    </Storyboard>
</DataGrid.Resources>
<DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsSaved}" Value="false">
                <Setter Property="Background" Value="#FFF4F496"/>
            </DataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding IsSaved}" Value="true"/>
                    <Condition Binding="{Binding IsAnimationPlayed}" Value="false"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.EnterActions>
                    <BeginStoryboard Name="Saved" Storyboard="{StaticResource AnimateYellowToTransparent}" />
                </MultiDataTrigger.EnterActions>
            </MultiDataTrigger>
        </Style.Triggers>
    </Style>
</DataGrid.RowStyle>

代码-behind的事件处理

private void AnimateYellowToTransparent_Completed(object sender, EventArgs e)
{
    var storyboard = sender as Storyboard;
    var row = storyboard.Target as DataGridRow;
    if (row?.DataContext is YourDataItemViewModel vm)
    {
        vm.IsAnimationPlayed = true;
    }
}

这样动画只会在IsSaved首次变为true时播放一次,后续行复用时因为IsAnimationPlayed已标记为true,不会重复触发。

方案2:使用附加属性标记动画状态(不修改ViewModel)

如果不想污染ViewModel,可以创建一个附加属性,在DataGridRow级别记录动画是否已播放,同时处理行复用的重置逻辑。

创建附加属性类

public static class DataGridRowAnimationHelper
{
    public static readonly DependencyProperty IsAnimationPlayedProperty =
        DependencyProperty.RegisterAttached(
            "IsAnimationPlayed",
            typeof(bool),
            typeof(DataGridRowAnimationHelper),
            new PropertyMetadata(false));

    public static bool GetIsAnimationPlayed(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsAnimationPlayedProperty);
    }

    public static void SetIsAnimationPlayed(DependencyObject obj, bool value)
    {
        obj.SetValue(IsAnimationPlayedProperty, value);
    }
}

修改后的XAML代码

<DataGrid.Resources>
    <Storyboard x:Key="AnimateYellowToTransparent" Completed="AnimateYellowToTransparent_Completed">
        <ColorAnimation Storyboard.TargetProperty="Background.(SolidColorBrush.Color)" 
                        From="#FFF4F496" To="Transparent"
                        Duration="0:0:1" RepeatBehavior="1x" />
    </Storyboard>
</DataGrid.Resources>
<DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsSaved}" Value="false">
                <Setter Property="Background" Value="#FFF4F496"/>
            </DataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding IsSaved}" Value="true"/>
                    <Condition Binding="{Binding Path=(local:DataGridRowAnimationHelper.IsAnimationPlayed), RelativeSource={RelativeSource Self}}" Value="false"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.EnterActions>
                    <BeginStoryboard Name="Saved" Storyboard="{StaticResource AnimateYellowToTransparent}" />
                </MultiDataTrigger.EnterActions>
            </MultiDataTrigger>
        </Style.Triggers>
        <EventSetter Event="DataContextChanged" Handler="DataGridRow_DataContextChanged"/>
    </Style>
</DataGrid.RowStyle>

代码-behind的事件处理

private void AnimateYellowToTransparent_Completed(object sender, EventArgs e)
{
    var storyboard = sender as Storyboard;
    var row = storyboard.Target as DataGridRow;
    if (row != null)
    {
        DataGridRowAnimationHelper.SetIsAnimationPlayed(row, true);
    }
}

private void DataGridRow_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    var row = sender as DataGridRow;
    if (row == null) return;

    // 行复用重置动画标记
    DataGridRowAnimationHelper.SetIsAnimationPlayed(row, false);
    // 如果新绑定的数据已保存,直接标记动画已播放
    if (row.DataContext is YourDataItemViewModel vm && vm.IsSaved)
    {
        DataGridRowAnimationHelper.SetIsAnimationPlayed(row, true);
    }
}
关键注意点
  1. 必须处理DataGrid行虚拟化带来的容器复用问题,这是动画重复播放的核心原因。
  2. 原始的ColorAnimation缺少To="Transparent",建议补充以明确动画目标颜色,避免依赖默认值。
  3. 无论哪种方案,核心都是通过一个“动画已播放”的标记,区分首次触发和后续行复用的场景。

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 20:02:39