Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当属性值在短窗口内多次更改时,DataTrigger不触发_C#_Wpf_Xaml_Animation_Datatrigger - Fatal编程技术网

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

C# 当属性值在短窗口内多次更改时,DataTrigger不触发,c#,wpf,xaml,animation,datatrigger,C#,Wpf,Xaml,Animation,Datatrigger,我在我的WPF应用程序中发现了一个有趣的问题,它与MultiDataTrigger没有启动故事板来设置数据网格单元的动画有关。我有一个WPF数据网格控件,它绑定到包含实现INotifyPropertyChanged的POCO的ObservableCollection 我想要达到的目标 一个实时数据网格,随着值的变化而闪烁更新。当值增加时,我希望单元格呈绿色闪烁;当值减小时,我希望电池闪烁红色。动画只是在1秒的时间间隔内将单元格的背景色从纯色设置为透明 问题 故事板在第一次启动后不会由MultiD

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

我想要达到的目标 一个实时数据网格,随着值的变化而闪烁更新。当值增加时,我希望单元格呈绿色闪烁;当值减小时,我希望电池闪烁红色。动画只是在1秒的时间间隔内将单元格的背景色从纯色设置为透明

问题 故事板在第一次启动后不会由MultiDataTrigger启动。MultiDataTrigger正在监视两个属性的更改:IsPositive和HasValueChanged。最初HasValueChanged为false,之后,一旦设置了Value属性,动画将从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>
表观修正 在SetValue方法中添加Thread.Sleep(100)设置HasValueChanged两次,似乎可以解决MultiDataTrigger未触发的问题,但会产生不希望的副作用

关于这个问题的视频 单击以查看已损坏版本的视频

单击以查看固定版本的视频

“固定”版本并不理想,因为Thread.Sleep会导致单元格以明显的顺序方式更新,而不是像损坏的版本那样同时更新。此外,有一根线。睡在那里让我感觉很糟糕:)

首先,;我这样做完全错了吗?有没有更简单/更好的方法来实现我的目标?如果不是,那么在不添加线程的情况下,这个问题的解决方案是什么呢

当值快速更改时,为什么WPF不会触发DataTrigger?是否存在导致WPF“错过”属性更改的因素;或者WPF只是忽略了在某个时间段内从一个值到另一个值再返回到原始值的更改吗


谢谢你的帮助

使用类似事件的属性似乎比
线程更糟糕。睡眠
对我来说,我建议将两个RoutedEvents(区分
更改+正
&
更改+负
)与事件触发器结合使用


如果你想坚持使用
线程。Sleep
你可能应该在后台这样做:

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

如果值在同一个dispatcher操作中再次更改,WPF可能会忽略该更改。不要睡觉,试着分派
HasValueChanged=true
部分。我真的不想使用Thread.Sleep,所以你的第一个建议听起来可能是正确的。但有一个问题:我可以在一个不从UIElement继承的类上引发RouteEvents吗?Cell类是一个不存在于可视化树中的POCO。对,这可能是个问题。如果使用普通事件,则无法使用默认的EventTrigger,Blend Interactive的EventTrigger将适用于每个事件,但在样式中使用它们可能非常困难…在MVVM应用程序中,最好在更改的属性上使用数据触发器。你知道你会怎么做吗?@Gusdor:不过你不应该拥有这样的房产。我不明白为什么不可以。CLR属性上的WPF绑定毕竟与OnPropertyChanged事件挂钩。这是一个描述USB设备是否已连接的布尔值-似乎是公开状态的合理方法。然而,我整理了我的问题。数据触发器始终正常触发,但加载后不会触发。我已经将eventtriggers与ConditionalBevious一起使用,以便在加载时应用正确的visualstates。
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); }
        }
    }
}
// IDE-free code, may be broken
HasValueChanged = false;
new Thread((ThreadStart)(() =>
{
    Thread.Sleep(100);
    HasValueChanged = true;
})).Start();