在VisualStates中重用动画

时间:2014-03-26 15:52:15

标签: silverlight xaml silverlight-5.0 resourcedictionary visualstatemanager

我想做什么(伪);

<!-- 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>

什么不起作用;见上文。

你可以在WPF中做到这一点,但在SL中没有任何乐趣。是否有相同的我失踪?

干杯!

1 个答案:

答案 0 :(得分:0)

我也尝试在VisualStates中重复使用Storyboard,但无济于事。 但是你有可以做的事情可以缓解这个问题: 为常用的Storyboard任务定义您自己的AttachedProperties(可附加到VisualState的实例),例如:

  • 在指定元素
  • 上设置Visibility
  • Opacity值应用于命名元素
  • 甚至通过故事板设置Focus。

xaml看起来像这样:

<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>

代码(对不起,它有点冗长):

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检查是否加载了控件:

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;
    }
}