Search code examples
wpfanimationstoryboardtransitionvisualstatemanager

Animating visibility AND opacity with wpf VisualStateManager


I am trying to animate a control so that it's visibility is set to visible then animating the opacity from 0 to 1

However nothing happens, then after 1 second the control is show with an opacity of 1... I cannot see what i am doing wrong

This is the code i have tried

<Grid x:Name="layout_root" Margin="10">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="Filtering">
            <VisualStateGroup.Transitions>
                <VisualTransition GeneratedDuration="0:0:1">
                    <VisualTransition.GeneratedEasingFunction>
                        <ElasticEase EasingMode="EaseInOut"/>
                    </VisualTransition.GeneratedEasingFunction>
                </VisualTransition>
            </VisualStateGroup.Transitions>

            <VisualState x:Name="Disabled"/>
            <VisualState x:Name="Enabled">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Visibility)">
                        <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
                    </ObjectAnimationUsingKeyFrames>
                    <DoubleAnimation Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </Grid.RowDefinitions>

    <TextBox x:Name="filter_control" Margin="0,0,0,10" Text="Filtering" Visibility="Collapsed" Opacity="0"/>

    <ListView Grid.Row="1" ItemsSource="{Binding Posts}">
        <ListView.View>
            <GridView>
                <GridViewColumn Width="100" Header="Date" DisplayMemberBinding="{Binding Date, StringFormat={}{0:dd/MM/yyyy}}"/>
                <GridViewColumn Width="100" Header="Text" DisplayMemberBinding="{Binding Text}"/>
                <GridViewColumn Width="100" Header="Value" DisplayMemberBinding="{Binding Value, StringFormat=F2}"/>
            </GridView>
        </ListView.View>
    </ListView>

    <Button Grid.Row="1" Content="v" FontFamily="Marlett" FontSize="14" VerticalAlignment="Top" HorizontalAlignment="Left" Click="ShowFilterClick"/>
</Grid>

Solution

  • As to the question of what you're doing wrong or why you see the behavior that you see: the storyboard for the Enabled state is the storyboard that the VSM uses while the VSGroup is in that state. You specify a transition storyboard for the group, though, and the VSM applies that when transitioning between states. So, when you put the VSGroup into the Enabled state, the VSM first plays the transition storyboard then uses the steady-state storyboard that you specify for the Enabled state. The transition storyboard is 1 sec, and that's why you're seeing the 1 sec delay and then the pop.

    Something like the following is probably what you want. Note that the transition storyboard does the action/animation that you want, and the state storyboards just state the final values at which the animated properties should be held. Also, I apply the easing function to the double animation rather than to the entire VisualTransition -- it doesn't make sense to try to interpolate Visibility with an easing function.

    <VisualStateGroup x:Name="Filtering">
        <VisualStateGroup.Transitions>
            <VisualTransition From="Disabled" To="Enabled" GeneratedDuration="0:0:1">
                <Storyboard>
                   <ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Visibility)">
                       <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
                   </ObjectAnimationUsingKeyFrames>
                   <DoubleAnimation Duration="0:0:1" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Opacity)" To="1">
                      <DoubleAnimation.EasingFunction>
                         <ElasticEase EasingMode="EaseInOut"/>
                      </DoubleAnimation.EasingFunction>
                   </DoubleAnimation>
               </Storyboard>
           </VisualTransition>
           <!-- you could also have a transition from Enabled to Disabled -->
        </VisualStateGroup.Transitions>
    
        <VisualState x:Name="Disabled">
                <Storyboard>
                   <ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Visibility)">
                       <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Collapsed}"/>
                   </ObjectAnimationUsingKeyFrames>
                   <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Opacity)" To="0"/>
               </Storyboard>
        </VisualState>
        <VisualState x:Name="Enabled">
                <Storyboard>
                   <ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Visibility)">
                       <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
                   </ObjectAnimationUsingKeyFrames>
                   <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
               </Storyboard>
        </VisualState>
    </VisualStateGroup>