如何判断故事StoryBoard

时间:2017-09-19 07:11:13

标签: wpf animation storyboard

最初的问题已经足够了 - “无法动画'(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,而是从登录方面看这些bug。我试图在InvalidOperationException中研究信息,但是没有像xaml或smth中的控件所在的有用信息。 另外一个想法是创建从Storyboard继承的类并覆盖方法。 但是没有方法可以覆盖。

有人可以提出如何记录故事板或其他负责动画的类的内部性吗?

1 个答案:

答案 0 :(得分:0)

最后我发现了闷闷不乐。 您应该添加类:侦听器到动画,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">