在 VisualStates 中重用动画 [英] Re-use Animations in VisualStates

查看:65
本文介绍了在 VisualStates 中重用动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做什么(伪);

<!-- As a resource -->
<Storyboard x:Key="RepetitiveAnimation">
   <DoubleAnimation .../>
</Storyboard>

<!-- In the templates -->
<VisualStateManager>
   <VisualState x:Name="blah" Storyboard="{StaticResource RepetitiveAnimation}"/>
</VisualStateManager>

甚至喜欢;

<VisualStateManager>
   <VisualState x:Name="blah">
      <BeginStoryboard Storyboard="{StaticResource RepetitiveAnimation}"/>
   </VisualState>
</VisualStateManager>

什么不起作用;见上文.

What doesnt work; See above.

您可以在 WPF 中执行此操作,但在 SL 中则没有乐趣.有没有我缺少的等价物?

You can do this in WPF, but in SL there's no joy. Is there an equivalent I'm missing?

干杯!

推荐答案

我也尝试在 VisualStates 中重用 Storyboard,但没有成功.但是您可以做的一件事可能会缓解这个问题:为常用的 Storyboard 任务定义您自己的 AttachedProperties(可附加到 VisualState 的实例),例如:

I tried reusing Storyboards in my VisualStates as well, but to no avail. But there is a thing you can do that may mitigate the problem: Define your own AttachedProperties (attachable to instances of VisualState) for frequently used Storyboard tasks, for example:

  • 在命名元素上设置可见性
  • Opacity 值应用到命名元素
  • 甚至通过故事板设置焦点.

xaml 看起来像这样:

The xaml would look like this:

<VisualStateGroup x:Name="TravelPlanningSteps">
    <VisualState x:Name="DateSelection"
        utils:VisualStateUtils.VisibleElements="DateSelector"
        utils:VisualStateUtils.FocusedElement="DateSelector"/>
    <VisualState x:Name="HotelSelection"
        utils:VisualStateUtils.VisibleElements="HotelSelector, BreakfastSelector"
        utils:VisualStateUtils.FocusedElement="HotelSelector"/>
</VisualStateGroup>

还有代码(抱歉,有点长):

And the code (sorry, it's a bit lengthy):

public static class VisualStateUtils
{
    #region FocusedElement

    public static string GetFocusedElement( VisualState obj )
    {
        return (string) obj.GetValue( FocusedElementProperty );
    }

    public static void SetFocusedElement( VisualState obj, string value )
    {
        obj.SetValue( FocusedElementProperty, value );
    }

    public static readonly DependencyProperty FocusedElementProperty =
        DependencyProperty.RegisterAttached( "FocusedElement", typeof( string ), typeof( VisualStateUtils ), new PropertyMetadata( HandleFocusedElementChanged ) );

    private static void HandleFocusedElementChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
        var state = d as VisualState;
        if (state == null) return;

        string elementName = e.NewValue as string;
        if (elementName == null) return;

        var storyBoard = state.Storyboard;
        if (storyBoard == null)
        {
            storyBoard = new Storyboard();
            state.Storyboard = storyBoard;
        }

        ClearAutoDefinedFocusClaim( storyBoard );

        var ani = new ObjectAnimationUsingKeyFrames();
        Storyboard.SetTargetName( ani, elementName );
        Storyboard.SetTargetProperty( ani, new PropertyPath( VisualStateUtils.FocusClaimProperty ) );
        VisualStateUtils.SetIsAutoDefinedFocusClaim( ani, true );
        ani.KeyFrames.Add( new DiscreteObjectKeyFrame { Value = true, KeyTime = KeyTime.FromTimeSpan( TimeSpan.FromMilliseconds( 0 ) ) } );
        storyBoard.Children.Add( ani );
    }

    public static bool GetFocusClaim( Control focusTarget )
    {
        return (bool) focusTarget.GetValue( FocusClaimProperty );
    }

    public static void SetFocusClaim( Control focusTarget, bool value )
    {
        focusTarget.SetValue( FocusClaimProperty, value );
    }

    public static readonly DependencyProperty FocusClaimProperty =
        DependencyProperty.RegisterAttached( "FocusClaim", typeof( bool ), typeof( VisualStateUtils ), new PropertyMetadata( false, HandleFocusClaimChanged ) );

    private static void HandleFocusClaimChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var focusTarget = d as Control;
        if(focusTarget==null) return;

        var shouldReceiveFocusNow = (bool) e.NewValue;

        if (shouldReceiveFocusNow)
        {
            if (!focusTarget.Focus()) CheckFocusability( focusTarget );
        }
    }

    private static void CheckFocusability(Control focusTarget)
    {
        //so the focus() call was not successful, what's the problem? lets see...
        //the control may still be collapsed
        //(and another animation will turn it visible anytime soon, remember: we are part of ongoing VisualState switching)
        if (!focusTarget.IsLoaded())
            focusTarget.Loaded += HandleFocusTargetLoaded;

        //it may be disabled (and another animation will enable it)
        else if (!focusTarget.IsEnabled)
            focusTarget.IsEnabledChanged += HandleFocusTargetEnabled;
    }

    private static void HandleFocusTargetLoaded( object sender, RoutedEventArgs routedEventArgs )
    {
        var focusTarget = (Control) sender;
        focusTarget.Loaded -= HandleFocusTargetLoaded;
        focusTarget.Focus();
    }

    private static void HandleFocusTargetEnabled(object sender, DependencyPropertyChangedEventArgs e)
    {
        var focusTarget = (Control) sender;
        focusTarget.IsEnabledChanged -= HandleFocusTargetEnabled;
        focusTarget.Focus();
    }

    public static bool GetIsAutoDefinedFocusClaim( DependencyObject obj )
    {
        return (bool) obj.GetValue( IsAutoDefinedFocusClaimProperty );
    }

    public static void SetIsAutoDefinedFocusClaim( DependencyObject obj, bool value )
    {
        obj.SetValue( IsAutoDefinedFocusClaimProperty, value );
    }

    public static readonly DependencyProperty IsAutoDefinedFocusClaimProperty =
        DependencyProperty.RegisterAttached( "IsAutoDefinedFocusClaim", typeof( bool ), typeof( VisualStateUtils ), new PropertyMetadata( false ) );

    private static void ClearAutoDefinedFocusClaim( Storyboard storyBoard )
    {
        var toDelete = storyBoard.Children.Where( VisualStateUtils.GetIsAutoDefinedFocusClaim ).ToList();
        toDelete.ForEach( animation => storyBoard.Children.Remove( animation ) );
    }

    #endregion

    #region CollapsedElements

    public static readonly DependencyProperty IsAutoCreatedCollapsedElementProperty =
        DependencyProperty.RegisterAttached( "IsAutoCreatedCollapsedElement", typeof( bool ), typeof( VisualStateUtils ), new PropertyMetadata( default( bool ) ) );

    private static void SetIsAutoCreatedCollapsedElement( DependencyObject element, bool value )
    {
        element.SetValue( IsAutoCreatedCollapsedElementProperty, value );
    }

    private static bool GetIsAutoCreatedCollapsedElement( DependencyObject element )
    {
        return (bool) element.GetValue( IsAutoCreatedCollapsedElementProperty );
    }

    public static readonly DependencyProperty CollapsedElementsProperty =
        DependencyProperty.RegisterAttached( "CollapsedElements", typeof( string ), typeof( VisualStateUtils ), new PropertyMetadata( default( string ), HandleCollapsedElementsChanged ) );

    private static void HandleCollapsedElementsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var state = d as VisualState;
        if (state == null) return;

        string elementNames = e.NewValue as string;
        if (elementNames == null) return;

        CreateAutoDefinedAnimationsForVisibility( Visibility.Collapsed, state, elementNames );
    }

    public static void SetCollapsedElements( VisualState state, string value )
    {
        state.SetValue( CollapsedElementsProperty, value );
    }

    public static string GetCollapsedElements( VisualState state )
    {
        return (string) state.GetValue( CollapsedElementsProperty );
    }

    #endregion

    #region VisibleElements

    public static readonly DependencyProperty VisibleElementsProperty =
        DependencyProperty.RegisterAttached( "VisibleElements", typeof( string ), typeof( VisualStateUtils ), new PropertyMetadata( default( string ), HandleVisibleElementsChanged ) );

    private static void HandleVisibleElementsChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
        var state = d as VisualState;
        if (state == null) return;

        string elementNames = e.NewValue as string;
        if (elementNames == null) return;

        CreateAutoDefinedAnimationsForVisibility( Visibility.Visible, state, elementNames );
    }

    public static void SetVisibleElements( VisualState state, string value )
    {
        state.SetValue( VisibleElementsProperty, value );
    }

    public static string GetVisibleElements( VisualState state )
    {
        return (string) state.GetValue( VisibleElementsProperty );
    }

    public static readonly DependencyProperty IsAutoCreatedVisibleElementProperty =
        DependencyProperty.RegisterAttached( "IsAutoCreatedVisibleElement", typeof( bool ), typeof( VisualStateUtils ), new PropertyMetadata( default( bool ) ) );

    private static void SetIsAutoCreatedVisibleElement( DependencyObject element, bool value )
    {
        element.SetValue( IsAutoCreatedVisibleElementProperty, value );
    }

    private static bool GetIsAutoCreatedVisibleElement( DependencyObject element )
    {
        return (bool) element.GetValue( IsAutoCreatedVisibleElementProperty );
    }

    #endregion

    private static void CreateAutoDefinedAnimationsForVisibility( Visibility visibility, VisualState state, string elementNames )
    {
        var storyBoard = state.Storyboard;
        if (storyBoard == null)
        {
            storyBoard = new Storyboard();
            state.Storyboard = storyBoard;
        }

        ClearAutoDefinedElementAnimations( visibility, storyBoard );

        string[] namesOfManipulatedElements = (elementNames ?? string.Empty).Split( ',' );
        namesOfManipulatedElements = namesOfManipulatedElements.Select( name => name.Trim() ).ToArray();
        foreach (var elementName in namesOfManipulatedElements)
        {
            var ani = new ObjectAnimationUsingKeyFrames();
            Storyboard.SetTargetName( ani, elementName );
            Storyboard.SetTargetProperty( ani, new PropertyPath( "Visibility" ) );
            MarkAutoDefinedElementAnimation( visibility, ani );
            ani.KeyFrames.Add( new DiscreteObjectKeyFrame { Value = visibility, KeyTime = KeyTime.FromTimeSpan( TimeSpan.FromMilliseconds( 0 ) ) } );
            storyBoard.Children.Add( ani );
        }
    }

    private static void ClearAutoDefinedElementAnimations( Visibility visibility, Storyboard storyBoard )
    {
        if (visibility == Visibility.Visible)
        {
            var toDelete = storyBoard.Children.Where( VisualStateUtils.GetIsAutoCreatedVisibleElement ).ToList();
            toDelete.ForEach( ani => storyBoard.Children.Remove( ani ) );
        }
        else
        {
            var toDelete = storyBoard.Children.Where( VisualStateUtils.GetIsAutoCreatedCollapsedElement ).ToList();
            toDelete.ForEach( ani => storyBoard.Children.Remove( ani ) );
        }
    }

    private static void MarkAutoDefinedElementAnimation( Visibility visibility, ObjectAnimationUsingKeyFrames animation )
    {
        if (visibility == Visibility.Visible)
            VisualStateUtils.SetIsAutoCreatedVisibleElement( animation, true );
        else
            VisualStateUtils.SetIsAutoCreatedCollapsedElement( animation, true );
    }
}

不要忘记使用方便的 Extension 来检查控件是否已加载:

And not to forget the handy Extension to check whether a control is loaded or not:

public static class ControlExtensions
{
    public static bool IsLoaded(this FrameworkElement element)
    {
        return element.GetVisualChildren().Any();
        //or just check the parent ...not sure what's better
        //return System.Windows.Media.VisualTreeHelper.GetParent(element) != null;
    }
}

这篇关于在 VisualStates 中重用动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆