开发者

Animating between visual states without an explosion of specific transitions

I have what seems to be a fairly simple scenario that I am trying to implement in Silverlight, but despite all the incredibly powerful transitions functionality in Silverlight/Blend 4, I just cannot work out how to do it.

I have a layout that boils down to this:

<UserControl>
    <Grid>
        <StackPanel>
            <Button x:Name="Button1" />
            <Button x:Name="Button2" />
            <Button x:Name="Button3" />
            <Button x:Name="Button4" />
        </StackPanel>
        <Grid x:Name="Page1" />
        <Grid x:Name="Page2" />
        <Grid x:Name="Page3" />
        <Grid x:Name="Page4" />
    </Grid>
</UserControl>

At first, all four page grids are hidden and scaled to zero size, but when you click its corresponding button, the page should appear with a growing animation. When you click another button, the previous page should disappear with a shrinking animation, and another page should appear with a growing animation. In this way, you can use the buttons to go between all four pages.

The "right" way to do this, from what I've read, is to use visual states on the user control. So I created four states, Page1 to Page4, and for each state s开发者_如何学Goet the appropriate page grid to display. I then put commands on the buttons to change the visual state of the user control. This worked fine, and I could switch between the pages, but when I started trying to define the animations between the states I had problems.

At first, I thought I could define a 'To *' and 'From *' animation for each state. So when you were in state Page1, and clicked on the button to go to state Page2, it would play the 'From *' animation hiding Page1, then a 'To *' animation displaying Page2. But this doesn't work. Even if you have defined a 'To *' and 'From *' animation for each state, Silverlight only plays the 'To *' animation, and completely ignores the 'From *' animation!

Even worse, it seems this behaviour is how Silverlight is supposed to work, even though it makes no sense at all! It means if I want to have each page shrink and then another page grow in its place, I would have to define a separate transition from each state to each other state! For my current four pages, this would mean twelve separate transitions, but when I want to increase the number of pages, this number will shoot up. Ten pages would require 9*9 = 81 transitions! All to get the current page to shrink, and the new page to grow.

I can't believe this there isn't a better way to handle what seems to be such a simple scenario, but nothing I can find seems to say how. I could probably hack it together using codebehind that modifies storyboards, but I want to allow viewing and editing the page grids in Blend, and also everything I read says to avoid using codebehind and use View Models and visual states to handle things

Please tell me I'm missing something obvious?


In Blend, you just click on the state in the States tab to start state recording, define what the state should look like, and set the state transition duration.

You shouldn't have to worry about each individual state transition permutation, unless you wanted each one to be different.

If your states use properties that can't be "linearly" animated (like changing Visibility), check the FluidLayout button.

Edit: You can create the "complete shrink and then grow" effect you describe using only one additional storyboard per state -- the Any -> {State} transition, setting the BeginTime to delay before growing the current element.

<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
mc:Ignorable="d"
x:Class="CommandingLeakWithScrollbar.UserControl1"
d:DesignWidth="640" d:DesignHeight="480">

<Grid x:Name="LayoutRoot">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="VisualStateGroup">
            <VisualStateGroup.Transitions>
                <VisualTransition GeneratedDuration="0:0:1"/>
                <VisualTransition GeneratedDuration="0:0:1" To="Page1">
                    <Storyboard>
                        <DoubleAnimation BeginTime="0:0:1" Duration="0:0:1" To="640" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="grid1" d:IsOptimized="True" />
                        <DoubleAnimation BeginTime="0:0:1" Duration="0:0:1" To="480" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid1" d:IsOptimized="True"/>
                    </Storyboard>
                </VisualTransition>
                <VisualTransition GeneratedDuration="0:0:1" To="Page2">
                    <Storyboard>
                        <DoubleAnimation BeginTime="0:0:1" Duration="0:0:1" To="640" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="grid2" d:IsOptimized="True" />
                        <DoubleAnimation BeginTime="0:0:1" Duration="0:0:1" To="480" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid2" d:IsOptimized="True"/>
                    </Storyboard>
                </VisualTransition>
                <VisualTransition From="None" GeneratedDuration="0:0:1" To="Page1"/>
                <VisualTransition From="None" GeneratedDuration="0:0:1" To="Page2"/>
            </VisualStateGroup.Transitions>
            <VisualState x:Name="None"/>
            <VisualState x:Name="Page1">
                <Storyboard>
                    <DoubleAnimation Duration="0" To="640" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="grid1" d:IsOptimized="True" />
                    <DoubleAnimation Duration="0" To="480" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid1" d:IsOptimized="True" />
                </Storyboard>
            </VisualState>
            <VisualState x:Name="Page2">
                <Storyboard>
                    <DoubleAnimation Duration="0" To="640" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="grid2" d:IsOptimized="True"/>
                    <DoubleAnimation Duration="0" To="480" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid2" d:IsOptimized="True"/>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <Grid x:Name="grid1"  Background="Beige" Width="40" Height="40" HorizontalAlignment="Left" VerticalAlignment="Top"/>
    <Grid x:Name="grid2" HorizontalAlignment="Right" Width="40" Height="40" VerticalAlignment="Top" Background="Wheat" />
    <Grid HorizontalAlignment="Left" Width="40" Height="40" VerticalAlignment="Bottom" />
    <Grid HorizontalAlignment="Right" Width="40" Height="40" VerticalAlignment="Bottom"/>
    <StackPanel HorizontalAlignment="Center">
        <Button Content="Reset" >
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <ei:GoToStateAction StateName="None"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
        <Button Content="Page1" >
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <ei:GoToStateAction StateName="Page1"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
        <Button Content="Page2" >
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <ei:GoToStateAction StateName="Page2"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </StackPanel>
</Grid>

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜