当属性值在短窗口内多次更改时,DataTrigger不会触发

时间:2011-06-14 22:07:42

标签: c# wpf xaml animation datatrigger

我在我的WPF应用程序中发现了一个有趣的问题,该问题与MultiDataTrigger相关,而不是启动StoryBoard来为数据网格单元设置动画。我有一个WPF数据网格控件,它绑定到包含实现INotifyPropertyChanged的POCO的ObservableCollection。

我想要实现的目标

实时数据网格,随着值的变化闪烁更新。当值增加时,我希望单元格闪烁绿色;当值减小时,我希望单元格闪烁红色。动画只需在1秒的时间间隔内将单元格的背景颜色从纯色动画显示为透明。

问题

第一次使用后,MultiDataTrigger不会启动故事板。 MultiDataTrigger正在监视对两个属性的更改:IsPositive和HasValueChanged。最初HasValueChanged为false,稍后在设置Value属性后将false从false更改为true,并且动画第一次运行。此后,HasValueChanged从false变为true以触发更改通知,但动画未启动。

这是我正在应用于每个数据网格单元的XAML样式:

<Style TargetType="{x:Type TextBlock}">
    <Style.Setters>
        <Setter Property="Background"
                Value="Aqua" />
    </Style.Setters>
    <Style.Triggers>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding Path=HasValueChanged}"
                            Value="True" />
                <Condition Binding="{Binding Path=IsPositive}"
                            Value="True" />
            </MultiDataTrigger.Conditions>
            <MultiDataTrigger.EnterActions>
                <RemoveStoryboard BeginStoryboardName="PositiveValueCellStoryboard" />
                <RemoveStoryboard BeginStoryboardName="NegativeValueCellStoryboard" />
                <BeginStoryboard Name="PositiveValueCellStoryboard"
                                    Storyboard="{StaticResource PositiveValueCellAnimation}"
                                    HandoffBehavior="SnapShotAndReplace" />
            </MultiDataTrigger.EnterActions>
        </MultiDataTrigger>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding Path=HasValueChanged}"
                            Value="True" />
                <Condition Binding="{Binding Path=IsPositive}"
                            Value="False" />
            </MultiDataTrigger.Conditions>
            <MultiDataTrigger.EnterActions>
                <RemoveStoryboard BeginStoryboardName="PositiveValueCellStoryboard" />
                <RemoveStoryboard BeginStoryboardName="NegativeValueCellStoryboard" />
                <BeginStoryboard Name="NegativeValueCellStoryboard"
                                    Storyboard="{StaticResource NegativeValueCellAnimation}"
                                    HandoffBehavior="SnapShotAndReplace" />
            </MultiDataTrigger.EnterActions>
        </MultiDataTrigger>
    </Style.Triggers>
</Style>

这是动画的XAML:

<Storyboard x:Key="NegativeValueCellAnimation">
    <ColorAnimation Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
                    Timeline.DesiredFrameRate="10"
                    RepeatBehavior="1x"
                    From="Red"
                    To="Transparent"
                    Duration="0:0:1" />
</Storyboard>

<Storyboard x:Key="PositiveValueCellAnimation">
    <ColorAnimation Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
                    Timeline.DesiredFrameRate="10"
                    RepeatBehavior="1x"
                    From="Green"
                    To="Transparent"
                    Duration="0:0:1" />
</Storyboard>

以下是绑定到每个单元格的POCO对象的代码:

using System;
using System.Threading;
using Microsoft.Practices.Prism.ViewModel;

namespace RealTimeDataGrid
{
    public class Cell : NotificationObject
    {
        public Cell(int ordinal, int value)
        {
            Ordinal = ordinal;
            _value = value;
            LastUpdated = DateTime.MaxValue;
        }

        public void SetValue(int value)
        {
            Value = value;

            // Pulse value changed to get WPF to fire DataTriggers
            HasValueChanged = false;
            Thread.Sleep(100);
            HasValueChanged = true;
        }

        private int _value;

        public int Value
        {
            get { return _value; }
            private set
            {
                if (_value == value)
                    return;

                _value = value;

                // Performance optimization, using lambdas here causes performance issues
                RaisePropertyChanged("IsPositive");
                RaisePropertyChanged("Value");
            }
        }

        private bool _hasValueChanged;

        public bool HasValueChanged
        {
            get { return _hasValueChanged; }
            set
            {
                if (_hasValueChanged == value)
                    return;

                _hasValueChanged = value;

                // Performance optimization, using lambdas here causes performance issues
                RaisePropertyChanged("HasValueChanged");
            }
        }

        public int Ordinal { get; set; }

        public DateTime LastUpdated { get; set; }

        public bool IsPositive
        {
            get { return Value >= 0; }
        }

        public TimeSpan TimeSinceLastUpdate
        {
            get { return DateTime.Now.Subtract(LastUpdated); }
        }
    }
}

表面修复

在SetValue方法中设置HasValueChanged两次之间添加Thread.Sleep(100)似乎可以解决MultiDataTrigger未触发的问题,但会产生不良副作用。

问题的视频

点击here查看已损坏版本的视频。

点击here查看固定版本的视频。

“固定”版本并不理想,因为Thread.Sleep导致单元格以明显顺序的方式更新,而不是像破碎版本那样同时更新。此外,在那里有一个Thread.Sleep让我觉得:)

首先;我错了吗?是否有更简单/更好的方法来实现我想要的?如果没有,这个问题的解决方案是什么,而不必诉诸于添加Thread.Sleep(代码味道!)?

为什么在快速更改值时WPF不会导致DataTrigger触发?是否存在导致WPF“错过”房产变化的事情;或者WPF是否只是忽略从一个值到另一个值的变化,然后在特定时间范围内回到原始值?

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

使用类似事件的属性似乎比我Thread.Sleep更糟糕的滥用,我建议使用两个RoutedEvents(区分Changed+Positive&amp; Changed+Negative)in与EventTriggers结合使用。


如果您想坚持使用Thread.Sleep,您应该在后台执行此操作:

// IDE-free code, may be broken
HasValueChanged = false;
new Thread((ThreadStart)(() =>
{
    Thread.Sleep(100);
    HasValueChanged = true;
})).Start();