【问题标题】:How determine fault StoryBoard如何判断故障 StoryBoard
【发布时间】:2018-02-27 20:47:30
【问题描述】:

最初的问题是众所周知的——“无法在不可变对象实例上为 '(0).(1)' 设置动画”。

这里有很多关于它的问题,但所有解决方案都是更多的修复或拐杖。而且大部分问题都与代码的具体部分相关联。

还有一些关于此问题的可能原因的主题: https://wpftutorial.net/DebuggingAnimations.html https://blogs.msdn.microsoft.com/mikehillberg/2006/09/25/tip-cannot-animate-on-an-immutable-object-instance/

我有一个巨大的企业应用程序,其中有数百个样式和故事板。我无法逐步禁用它们,寻找代码的问题部分是一项艰苦的工作。

我不是从在许多 xamls 中寻找这些错误的角度,而是从日志记录的角度来看这些错误。我试图研究引发的 InvalidOperationException 中的信息,但没有有用的信息,例如 xaml 或其他 smth 中的控制位置。 还有一个想法是创建从 Storyboard 继承的类并覆盖方法。 但是没有方法可以覆盖。

有人可以建议如何记录情节提要或其他负责动画的类的内部性吗?

【问题讨论】:

  • 正如我所写,我无法确定 xaml 的问题部分,因此无法提供示例,当然我也无法上传源代码。这就是为什么我问记录所有故事板的方式。

标签: wpf animation storyboard


【解决方案1】:

我终于找到了解决办法。 您应该添加类:动画侦听器、AttachedProperty 和自定义 StoryBoard。

public static class TriggerTracing
{
    static TriggerTracing()
    {
        // Initialise WPF Animation tracing and add a TriggerTraceListener
        PresentationTraceSources.Refresh();
        PresentationTraceSources.AnimationSource.Listeners.Clear();
        PresentationTraceSources.AnimationSource.Listeners.Add(new TriggerTraceListener());
        PresentationTraceSources.AnimationSource.Switch.Level = SourceLevels.All;
    }

    #region TriggerName attached property

    /// <summary>
    /// Gets the trigger name for the specified trigger. This will be used
    /// to identify the trigger in the debug output.
    /// </summary>
    /// <param name="trigger">The trigger.</param>
    /// <returns></returns>
    public static string GetTriggerName(TriggerBase trigger)
    {
        return (string)trigger.GetValue(TriggerNameProperty);
    }

    /// <summary>
    /// Sets the trigger name for the specified trigger. This will be used
    /// to identify the trigger in the debug output.
    /// </summary>
    /// <param name="trigger">The trigger.</param>
    /// <returns></returns>
    public static void SetTriggerName(TriggerBase trigger, string value)
    {
        trigger.SetValue(TriggerNameProperty, value);
    }

    public static readonly DependencyProperty TriggerNameProperty =
        DependencyProperty.RegisterAttached(
        "TriggerName",
        typeof(string),
        typeof(TriggerTracing),
        new UIPropertyMetadata(string.Empty));

    #endregion

    #region TraceEnabled attached property

    /// <summary>
    /// Gets a value indication whether trace is enabled for the specified trigger.
    /// </summary>
    /// <param name="trigger">The trigger.</param>
    /// <returns></returns>
    public static bool GetTraceEnabled(TriggerBase trigger)
    {
        return (bool)trigger.GetValue(TraceEnabledProperty);
    }

    /// <summary>
    /// Sets a value specifying whether trace is enabled for the specified trigger
    /// </summary>
    /// <param name="trigger"></param>
    /// <param name="value"></param>
    public static void SetTraceEnabled(TriggerBase trigger, bool value)
    {
        trigger.SetValue(TraceEnabledProperty, value);
    }

    public static readonly DependencyProperty TraceEnabledProperty =
        DependencyProperty.RegisterAttached(
        "TraceEnabled",
        typeof(bool),
        typeof(TriggerTracing),
        new UIPropertyMetadata(false, OnTraceEnabledChanged));

    private static void OnTraceEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var triggerBase = d as EventTrigger;

        if (triggerBase == null)
            return;

        if (!(e.NewValue is bool))
            return;

        if ((bool)e.NewValue)
        {
            // insert dummy story-boards which can later be traced using WPF animation tracing

            var storyboard = new TriggerTraceStoryboard(triggerBase, TriggerTraceStoryboardType.Enter);
            triggerBase.Actions.Insert(0, new BeginStoryboard() { Storyboard = storyboard });

            //storyboard = new TriggerTraceStoryboard(triggerBase, TriggerTraceStoryboardType.Exit);
            //triggerBase.ExitActions.Insert(0, new BeginStoryboard() { Storyboard = storyboard });
        }
        else
        {
            // remove the dummy storyboards
            //foreach (TriggerActionCollection actionCollection in new[] { triggerBase.EnterActions, triggerBase.ExitActions })
            foreach (TriggerActionCollection actionCollection in new[] { triggerBase.Actions })
            {
                foreach (TriggerAction triggerAction in actionCollection)
                {
                    BeginStoryboard bsb = triggerAction as BeginStoryboard;

                    if (bsb != null && bsb.Storyboard != null && bsb.Storyboard is TriggerTraceStoryboard)
                    {
                        actionCollection.Remove(bsb);
                        break;
                    }
                }
            }
        }
    }

    #endregion

    private enum TriggerTraceStoryboardType
    {
        Enter, Exit
    }

    /// <summary>
    /// A dummy storyboard for tracing purposes
    /// </summary>
    private class TriggerTraceStoryboard : Storyboard
    {
        public TriggerTraceStoryboardType StoryboardType { get; private set; }
        public TriggerBase TriggerBase { get; private set; }

        public TriggerTraceStoryboard(TriggerBase triggerBase, TriggerTraceStoryboardType storyboardType)
        {
            TriggerBase = triggerBase;
            StoryboardType = storyboardType;
        }
    }

    /// <summary>
    /// A custom tracelistener.
    /// </summary>
    private class TriggerTraceListener : TraceListener
    {
        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
        {
            base.TraceEvent(eventCache, source, eventType, id, format, args);

            if (format.StartsWith("Storyboard has begun;"))
            {
                TriggerTraceStoryboard storyboard = args[1] as TriggerTraceStoryboard;
                if (storyboard != null)
                {
                    // add a breakpoint here to see when your trigger has been
                    // entered or exited

                    // the element being acted upon
                    object targetElement = args[5];

                    // the namescope of the element being acted upon
                    INameScope namescope = (INameScope)args[7];

                    TriggerBase triggerBase = storyboard.TriggerBase;
                    string triggerName = GetTriggerName(storyboard.TriggerBase);

                    var str = "";
                    var element = targetElement as DependencyObject;
                    while (element != null)
                    {
                        str += element.ToString() + Environment.NewLine;
                        element = VisualTreeHelper.GetParent(element);
                    }

                    LoggingInfrastructure.DefaultLogger.Log(...);
                }
            }
        }

        public override void Write(string message)
        {
        }

        public override void WriteLine(string message)
        {
        }
    }

然后您可以在需要的地方添加属性到 xaml:

<EventTrigger Ui:TriggerTracing.TriggerName="CopyTextBlockStyle PreviewMouseLeftButtonDown"  
 Ui:TriggerTracing.TraceEnabled="True"  RoutedEvent="PreviewMouseLeftButtonDown">

【讨论】:

    猜你喜欢
    • 2015-05-12
    • 1970-01-01
    • 1970-01-01
    • 2017-06-23
    • 2021-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多