LinearGradientBrush中的停止动画不一致错误

时间:2014-09-08 16:24:28

标签: c# wpf xaml animation storyboard

更新 我可能知道是什么导致了它,但我仍然想了解为什么以及是否有更好的方法来做到这一点。

原因似乎是传递给转换器的值的IsAsync =“True”。我注意到每个警报都会调用StatusBackgroundColorConverter2 3到4次。

  • 第一次进入转换方法时,Value和IsAcknowledge都设置为DependencyProperty.UnsetValue。
  • 第二次有Value属性,但IsAcknowledged是Unset。
  • 第三次通过Value和IsAcknowledge终于确定了。

因此,当MultiBinding中的各个Bindings设置为Async时,这意味着您的转换器转换方法会在任何绑定发生变化时随时运行。

我添加了另一个绑定用于调试以查看Item本身。更改值时,执行转换方法并填充值,但IsAcknowledged是UnsetValue,即使我可以看到在实际对象上设置了IsAcknowledged。

我可以关闭IsAsync,但这会导致状态发生变化时UI显着滞后。是否有某种方法可以为绑定本身打开IsAsync,为其中的各个绑定关闭IsAsync?

END UPDATE

我是一个真正的泡菜,拼命想要了解这里的问题。任何帮助将不胜感激!我有一个关键的应用程序,它监视几件事情并根据数据显示警报通知。

警报显示在ItemsControls中。各种显示属性(如背景和前景颜色)会根据状态发生变化。某些状态需要“闪烁”,我根据状态(黄色,红色等)以适当的颜色实现滚动渐变。

所有这些功能实际上都有效,但是一段时间后警报开始抛出以下错误:

  

'GradientStops'在路径'(0)中的属性值。(1)[1]。(2)'指向   'System.Windows.Media.GradientStopCollection'的不可变实例。

暂时误解了该错误,但该警报不再有效。他们一个接一个地死在这里。为了重新创建这个,我必须设置数据来每5秒钟来回更改许多警报的状态。 30到60秒后,它开始发生。

矩形Xaml

<Rectangle Grid.Column="1" Grid.Row="0"    >
                            <Rectangle.Fill  >
                                <MultiBinding UpdateSourceTrigger="PropertyChanged"  NotifyOnTargetUpdated="True" NotifyOnSourceUpdated="True">
                                    <MultiBinding.Converter >
                                        <conv:StatusBackgroundColorConverter2 />
                                    </MultiBinding.Converter>
                                    <Binding Path="Value" IsAsync="True" UpdateSourceTrigger="PropertyChanged" NotifyOnTargetUpdated="True" NotifyOnSourceUpdated="True"  />
                                    <Binding Path="IsAcknowledged" IsAsync="True" UpdateSourceTrigger="PropertyChanged" NotifyOnTargetUpdated="True" NotifyOnSourceUpdated="True" />
                                </MultiBinding>
                            </Rectangle.Fill>
                            <Rectangle.Style>
                                <Style>
                                    <Style.Triggers>
                                        <DataTrigger Value="true"  >
                                            <DataTrigger.Binding>
                                                <MultiBinding UpdateSourceTrigger="PropertyChanged">
                                                    <MultiBinding.Converter>
                                                        <conv:IsBlinkingStatusConverter />
                                                    </MultiBinding.Converter>
                                                    <Binding Path="Value" IsAsync="True" UpdateSourceTrigger="PropertyChanged" />
                                                    <Binding Path="IsAcknowledged" IsAsync="True" UpdateSourceTrigger="PropertyChanged" />
                                                </MultiBinding>
                                            </DataTrigger.Binding>
                                            <DataTrigger.EnterActions>
                                                <RemoveStoryboard BeginStoryboardName="RollingGradient"  />
                                                <BeginStoryboard Name="RollingGradient">
                                                    <Storyboard RepeatBehavior="Forever">
                                                        <DoubleAnimation FillBehavior="Stop" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Offset)" From="-.1" To="1.3" By=".1" Duration="0:0:3"/>
                                                        <DoubleAnimation FillBehavior="Stop" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Offset)" From=".6" To="1.3" By=".1" Duration="0:0:3"/>
                                                    </Storyboard>
                                                </BeginStoryboard>
                                            </DataTrigger.EnterActions>
                                            <DataTrigger.ExitActions>
                                                <RemoveStoryboard BeginStoryboardName="RollingGradient"  />
                                            </DataTrigger.ExitActions>
                                        </DataTrigger>                                             
                                    </Style.Triggers>
                                </Style>
                            </Rectangle.Style>
                        </Rectangle>

转换器:

public class StatusBackgroundColorConverter2 : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values != null && values.Length > 1 && values[0] != DependencyProperty.UnsetValue && values[1] != DependencyProperty.UnsetValue)
        {
            Brush brush;
            int status = System.Convert.ToInt32(values[0]);
            bool acknowledged = System.Convert.ToBoolean(values[1]);
            bool blink = blinkingStatuses.Contains(status) && !acknowledged; 

            Color c = new Color();

            if (status < 0 || status > 12)
                c = Color.FromRgb(255, 255, 255);
            else if (status == 0)
                c = Color.FromRgb(0, 0, 0); 
            else if (status < 5)
                c = Color.FromRgb(255, 255, 80);
            else if (status < 9)
                c = Color.FromRgb(255, 153, 0);
            else if (status < 13)
            {
                c = Color.FromRgb(255, 38, 0);
            }

            if (blink)
            { 
                LinearGradientBrush lgb = new LinearGradientBrush();
                lgb.StartPoint = new Point(0, 0.5);
                lgb.EndPoint = new Point(1, 0.5); 
                Color backcolor = Color.FromRgb(0, 0, 0);

                lgb.GradientStops.Add(new GradientStop { Color = backcolor, Offset = 0 });
                lgb.GradientStops.Add(new GradientStop { Color = c, Offset = -.1 });
                lgb.GradientStops.Add(new GradientStop { Color = backcolor, Offset = .6 });

                brush = lgb;
            }
            else
            // I was returning a SolidColorBrush if status was not blinking, but I changed to gradient brush and 
            // and just kept the colors the same hoping having the stops there would address the issue.  It didn't.
            // brush = new SolidColorBrush(c);
            {
                var b = new LinearGradientBrush();
                b.GradientStops.Add(new GradientStop { Color = c, Offset = 0});
                b.GradientStops.Add(new GradientStop { Color = c, Offset = -.1 });
                b.GradientStops.Add(new GradientStop { Color = c, Offset = .6 });

                brush = b;
            }

            return brush;
        }
        return new SolidColorBrush(Color.FromRgb(255, 255, 255));   
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }

    private int[] blinkingStatuses = new int[] { 3, 4, 7, 8, 11, 12 };
}

对我来说最困难的部分是它起初工作得很好。如果它会失败,为什么它不会第一次失败?我真诚地感谢任何对此的投入。我必须想出一些东西,我觉得我很少继续下去。

谢谢!

1 个答案:

答案 0 :(得分:0)

我的问题的关键在于使用带有异步绑定的多值转换器。以下面的例子为例:

<MultiBinding UpdateSourceTrigger="PropertyChanged">
                                                <MultiBinding.Converter>
                                                    <conv:IsBlinkingStatusConverter />
                                                </MultiBinding.Converter>
                                                <Binding Path="Value" IsAsync="True" UpdateSourceTrigger="PropertyChanged" />
                                                <Binding Path="IsAcknowledged" IsAsync="True" UpdateSourceTrigger="PropertyChanged" />
                                            </MultiBinding>

我认为我的多值转换器中的转换函数只有在所有参数值可用并且具有所需要的值后才会执行一次。事实并非如此。

我发现的是,对于我的集合中的每个项目,我的Convert方法被调用了3次。

我的所有参数第一次是DependencyProperty.UnsetValue。 第二次设置了值,但IsAcknowledged是DependencyProperty.UnsetValue。 第三次设置两个属性。

没有值转换器中的特定逻辑返回Binding.DoNothing它执行了两次不完整的数据,然后最后一次执行所有参数。所以你的结果基本上是坏,坏,好。当系统没有负载时,这种情况发生得非常快,所以你没有注意到它。但是在负载下,呼叫之间的间隔会稍长一些。它处于不良状态的时间越长,其他东西在这种无效状态下可能会起作用的可能性越大。

我尝试指定默认值甚至取出IsAsync属性。虽然它确实有效但在屏幕更新时会造成明显的延迟。在执行逻辑之前将它们放回并实现逻辑以查找所有必要的值,效果非常好。