Анимация при изменении видимости: свойство уже было зарегистрировано «FrameworkElement»

Я использую этот код для установки анимации при изменении видимости.

public class VisibilityAnimation : DependencyObject
{
    public enum AnimationType
    {
        None,
        Fade
    }

    private const int AnimationDuration = 1000;

    private static readonly Dictionary<FrameworkElement, bool> _hookedElements =
        new Dictionary<FrameworkElement, bool>();

    public static AnimationType GetAnimationType(DependencyObject obj)
    {
        return (AnimationType)obj.GetValue(AnimationTypeProperty);
    }

    public static void SetAnimationType(DependencyObject obj, AnimationType value)
    {
        obj.SetValue(AnimationTypeProperty, value);
    }

    public static readonly DependencyProperty AnimationTypeProperty =
        DependencyProperty.RegisterAttached(
            "AnimationType",
            typeof(AnimationType),
            typeof(VisibilityAnimation),
            new FrameworkPropertyMetadata(AnimationType.None,
                new PropertyChangedCallback(OnAnimationTypePropertyChanged)));
    private static void OnAnimationTypePropertyChanged(
        DependencyObject dependencyObject,
        DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement frameworkElement = dependencyObject as FrameworkElement;

        if (frameworkElement == null)
        {
            return;
        }

        // If AnimationType is set to True on this framework element, 
        if (GetAnimationType(frameworkElement) != AnimationType.None)
        {
            // Add this framework element to hooked list
            HookVisibilityChanges(frameworkElement);
        }
        else
        {
            // Otherwise, remove it from the hooked list
            UnHookVisibilityChanges(frameworkElement);
        }
    }
    private static void HookVisibilityChanges(FrameworkElement frameworkElement)
    {
        _hookedElements.Add(frameworkElement, false);
    }
    private static void UnHookVisibilityChanges(FrameworkElement frameworkElement)
    {
        if (_hookedElements.ContainsKey(frameworkElement))
        {
            _hookedElements.Remove(frameworkElement);
        }
    }
    static VisibilityAnimation()
    {
        // Here we "register" on Visibility property "before change" event
        UIElement.VisibilityProperty.AddOwner(
            typeof(FrameworkElement),
            new FrameworkPropertyMetadata(
                Visibility.Visible,
                VisibilityChanged,
                CoerceVisibility));

    }
    private static void VisibilityChanged(
        DependencyObject dependencyObject,
        DependencyPropertyChangedEventArgs e)
    {
        // Ignore
    }
    private static object CoerceVisibility(
        DependencyObject dependencyObject,
        object baseValue)
    {
        // Make sure object is a framework element
        FrameworkElement frameworkElement = dependencyObject as FrameworkElement;
        if (frameworkElement == null)
        {
            return baseValue;
        }

        // Cast to type safe value
        Visibility visibility = (Visibility)baseValue;

        // If Visibility value hasn't change, do nothing.
        // This can happen if the Visibility property is set using data binding 
        // and the binding source has changed but the new visibility value 
        // hasn't changed.
        if (visibility == frameworkElement.Visibility || visibility == Visibility.Collapsed) //Aggiungo da cri..x fare l'effetto solo sul fade in
        {
            return baseValue;
        }

        // If element is not hooked by our attached property, stop here
        if (!IsHookedElement(frameworkElement))
        {
            return baseValue;
        }

        // Update animation flag
        // If animation already started, don't restart it (otherwise, infinite loop)
        if (UpdateAnimationStartedFlag(frameworkElement))
        {
            return baseValue;
        }

        // If we get here, it means we have to start fade in or fade out animation. 
        // In any case return value of this method will be Visibility.Visible, 
        // to allow the animation.
        DoubleAnimation doubleAnimation = new DoubleAnimation
        {
            Duration = new Duration(TimeSpan.FromMilliseconds(AnimationDuration))
        };

        // When animation completes, set the visibility value to the requested 
        // value (baseValue)
        doubleAnimation.Completed += (sender, eventArgs) =>
        {
            if (visibility == Visibility.Visible)
            {
                // In case we change into Visibility.Visible, the correct value 
                // is already set, so just update the animation started flag
                UpdateAnimationStartedFlag(frameworkElement);
            }
            else
            {
                // This will trigger value coercion again 
                // but UpdateAnimationStartedFlag() function will reture true 
                // this time, thus animation will not be triggered. 
                if (BindingOperations.IsDataBound(frameworkElement,
                    UIElement.VisibilityProperty))
                {
                    // Set visiblity using bounded value
                    Binding bindingValue =
                        BindingOperations.GetBinding(frameworkElement,
                            UIElement.VisibilityProperty);
                    BindingOperations.SetBinding(frameworkElement,
                        UIElement.VisibilityProperty, bindingValue);
                }
                else
                {
                    // No binding, just assign the value
                    frameworkElement.Visibility = visibility;
                }
            }
        };

        if (visibility == Visibility.Collapsed || visibility == Visibility.Hidden)
        {
            // Fade out by animating opacity
            doubleAnimation.From = 1.0;
            doubleAnimation.To = 0.0;
        }
        else
        {
            // Fade in by animating opacity
            doubleAnimation.From = 0.0;
            doubleAnimation.To = 1.0;
        }

        // Start animation
        frameworkElement.BeginAnimation(UIElement.OpacityProperty, doubleAnimation);

        // Make sure the element remains visible during the animation
        // The original requested value will be set in the completed event of 
        // the animation
        return Visibility.Visible;
    }
    private static bool IsHookedElement(FrameworkElement frameworkElement)
    {
        return _hookedElements.ContainsKey(frameworkElement);
    }
    private static bool UpdateAnimationStartedFlag(FrameworkElement frameworkElement)
    {
        bool animationStarted = (bool)_hookedElements[frameworkElement];
        _hookedElements[frameworkElement] = !animationStarted;

        return animationStarted;
    }

В xaml мне нужно установить: VisibilityAnimation.AnimationType="Fade"

Анимация работает нормально, но проблема в том, что у меня ошибка в заголовке.

Как я могу это исправить?

Stack Overflow хочет больше деталей, чтобы вставить этот код, но это все... напишите эту фразу, надеясь, что я смогу ее вставить.


person Atomico    schedule 23.08.2014    source источник
comment
Диспетчерский таймер тут не сработает? потому что я использовал это в Windows Mobile 8. И используйте непрозрачность (для видимости) для создания собственной анимации.   -  person gayan1991    schedule 23.08.2014
comment
Искать постепенное появление / исчезновение в зависимости от видимости изменилось? или забота больше, чем это?   -  person pushpraj    schedule 30.08.2014
comment
я хочу понять, как исправить эту ошибку, но главное, что я хочу сделать, это эффект затухания ... если у вас есть альтернатива, которую я могу использовать, я ценю это   -  person Atomico    schedule 30.08.2014
comment
вы ограничены использованием только Visibility? Вы согласны использовать другой триггер, например, FadeHelper.Visibility?   -  person pushpraj    schedule 31.08.2014


Ответы (2)


Согласно MSDN (http://msdn.microsoft.com/en-us/library/ms754209%28v=vs.110%29.aspx) OverrideMetadata (связанные с AddOwner) следует вызывать только из статического конструктора того типа, у которого свойство Metadata переопределено. Я предполагаю, что это правило одинаково для метода AddOwner свойства зависимости.

Кстати, вам действительно нужен такой широкий крюк приложения? На основе кода, который у вас есть, похоже, что вы действительно ищете уведомление об изменении видимости для FrameworkElement, которому назначено ваше прикрепленное свойство. Вы можете добиться этого следующим образом. DependencyPropertyDescriptor.FromProperty(UIElement.VisibilityProperty, typeof(FrameworkElement)).AddValueChange(frameworkElement, обратный вызов)

Но поскольку то, что вы ищете, представляет собой широкий крючок приложения для обратного вызова значения ядер видимости для FrameworkElements, вы можете сделать что-то вроде этого

    static VisibilityAnimation()
    {
        // Here we "register" on Visibility property "before change" event
        var desc = DependencyPropertyDescriptor.FromProperty(UIElement.VisibilityProperty, typeof(FrameworkElement));
        desc.DesignerCoerceValueCallback += CoerceVisibility;

Кстати, существует высокая вероятность того, что FrameworkElements, к которым подключено это свойство, не будут собирать мусор, поскольку они находятся в словаре _hookedElements до тех пор, пока свойство AnimationType не изменится на AnimationType.None.

person dharshana jagoda    schedule 03.09.2014
comment
это работает! Спасибо! p.s. с ваших слов я так понимаю, что эта стратегия не очень хороша? есть ли альтернатива добавлению эффекта постепенного изменения видимости? - person Atomico; 04.09.2014
comment
Проблема в следующем: делает UnHookVisibilityChanges(frameworkElement); когда-либо вызывали на практике? Если нет, можете ли вы убедиться, что он будет вызван? Если ответ на оба вопроса отрицательный, вам, возможно, придется использовать WeakDictionary, подобный этому blogs.msdn.com/b/nicholg/archive/2006/06/04/617466.aspx для предотвращения утечек памяти. - person dharshana jagoda; 04.09.2014

В своем статическом конструкторе измените тип владельца на свой класс VisibilityAnimation

static VisibilityAnimation()
{
    // Here we "register" on Visibility property "before change" event
    UIElement.VisibilityProperty.AddOwner(
        typeof(VisibilityAnimation),
        new FrameworkPropertyMetadata(
            Visibility.Visible,
            VisibilityChanged,
            CoerceVisibility));

}
person Roel van Westerop    schedule 28.08.2014
comment
сделал, но не работает. Теперь в xaml у меня есть ошибка о том, что AnimationType должен быть получен из DependenceObject - person Atomico; 28.08.2014
comment
@Atomico Я не получаю никаких ошибок, может быть, вы могли бы указать XAML или код, чтобы показать, как вы используете VisibilityAnimation. - person Roel van Westerop; 28.08.2014
comment
код xaml, который получает ошибку, - это тот, который я публикую в первом потоке: VisibilityAnimation.AnimationType=Fade - person Atomico; 28.08.2014
comment
@Atomico Да, но на самом деле это не дает никакого представления о том, как вы его используете. Вы указываете пространство имен, какое точное сообщение об ошибке и т. д.? Потому что это работает для меня: ‹Grid local:VisibilityAnimation.AnimationType=Fade Background=Red /› - person Roel van Westerop; 29.08.2014
comment
Ну, ты видишь анимацию? теперь он не дает никаких ошибок, но не показывает анимацию. - person Atomico; 29.08.2014
comment
@Atomico Да, я вижу анимацию, если переключаю видимость сетки между видимой и скрытой. - person Roel van Westerop; 29.08.2014
comment
Можете ли вы вставить код, который вы используете для изменения видимости, пожалуйста - person Atomico; 29.08.2014
comment
@Atomico ‹Grid› ‹CheckBox x:Name=chk /› ‹Grid Background=Red local:VisibilityAnimation.AnimationType=Fade Margin=0,30,0,0 Visibility={Binding ElementName=chk, Path=IsChecked, Converter={ StaticResource conv}}/›‹/Grid›. conv — это BooleanToVisibilityConverter. - person Roel van Westerop; 29.08.2014
comment
Я меняю видимость из кода программной части (событие кнопки).. это что-то меняет? - person Atomico; 29.08.2014
comment
Нет, у меня все еще работает, когда я переключаю видимость в обработчике событий нажатия кнопки. - person Roel van Westerop; 02.09.2014