WPF Storyboard - same trigger, but reverse behavior

北城余情 提交于 2019-12-10 10:17:55

问题


I have a Stackpanel One, which has some content, an Image, and a defualt hidden SubStackpanel. When clickin the Image, the image should rotate 90 degrees, and slide down the SubStackpanel. When clicking the Image again, the Image should rotate back to its original position, and the SubStackpanel should slide up to the default hidden position.

I almost got this working, the problem is that I dont know how to use the same Trigger event, on two different Storyboard animations. So right now only the first animation on the button and the SubStackpanel occurs, everytime the Image is clicked. I´ve tried the AutoReverse property, but it fires immediately after the animation is done. This should of course only be happening when the user clicks the Image the second time.

I would like to achieve this, only using markup.

This is my currently code:

<Grid>
        <StackPanel Grid.Row="0" Orientation="Vertical" Background="Beige" >
            <StackPanel.Triggers>
                <EventTrigger SourceName="ImageShowPanelTwo" RoutedEvent="Image.MouseDown">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetName="SubPanel" Storyboard.TargetProperty="(StackPanel.Height)" From="0" To="66" Duration="0:0:0.5" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <EventTrigger  SourceName="ImageShowPanelTwo" RoutedEvent="Image.MouseDown">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetName="SubPanel" Storyboard.TargetProperty="(StackPanel.Height)" From="66" To="0" Duration="0:0:0.5" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </StackPanel.Triggers>

            <TextBlock>Panel One</TextBlock>

            <Image Name="ImageShowPanelTwo" Width="26" Height="26" Source="ImageRotate.png" RenderTransformOrigin=".5,.5"  >
                <Image.RenderTransform>
                    <RotateTransform x:Name="AnimatedRotateTransform" Angle="0" />
                </Image.RenderTransform>
                <Image.Triggers>
                    <EventTrigger RoutedEvent="Image.MouseDown">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetName="AnimatedRotateTransform" 
                                             Storyboard.TargetProperty="Angle" 
                                             By="0"        
                                             To="90" 
                                             Duration="0:0:0.5" 
                                                 AutoReverse="True"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    <EventTrigger RoutedEvent="Image.MouseDown">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetName="AnimatedRotateTransform" 
                                             Storyboard.TargetProperty="Angle" 
                                             By="90"        
                                             To="0" 
                                             Duration="0:0:0.5" 
                                                 AutoReverse="True"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Image.Triggers>
            </Image>


            <StackPanel Name="SubPanel" Background="LightGreen" Height="66">
                <TextBlock>SubPanel</TextBlock>
                <TextBlock>SubPanel</TextBlock>
            </StackPanel>

        </StackPanel>


    </Grid>

Hope you can help :)


回答1:


Blend for Visual Studio can be used to do it in a better, simpler way, without dealing with a lot of code.

Here is the list of steps of doing it.

Step 1: Open the project in Blend for Visual Studio and in Visual Studio simultaneously.

Step 2: In Blend, create a new storyboard around all the elements that you wish to animate with it. Let's call it "Storyboard1".

Step 3: Now, open the storyboard that we just created and click on the small arrow just beside the "+" and click on "Duplicate" in the drop down menu. Then, a new storyboard called "Storyboard1_Copy" will be created.

Step 4: Rename this new storyboard to something that you like, say, "Storyboard1_Rev".

Step 5: You must have guessed it by now. Select the duplicated storyboard and from the drop down menu, click on "Reverse".

Step 6: Now you have two storyboards ready: one for animating some elements as you like and the other for reversing that sequence of animation. Just like you call a storyboard from the C# code, you can call the reversing storyboard from the same, subject to some conditions which check if the elements are already animated or not. For this, I use a bool variable, whose value is changed each time some animation occurs on a set of elements(that is, false if the elements are not already animated and true if they are).

Illustration with an example:

I'll create an application with a simple layout. It has a button, an image and a rectangular area on the screen.

The idea is that, whenever you click on the button once, the image should be maximized and should be minimized back to original size when the button is clicked twice and so on. That is, the reverse animation should happen every other time the button is clicked. Here are some screenshots showing how it happens:

You can see that the current state of the image is shown in the button. It shows "Zoom In" when the image is in its initial small size and "Zoom Out" when it is maximized.

And finally, here is the C# code for handling the clicks of the button:

    bool flag = false;
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (!flag)
        {
            Button.Content = "Zoom Out";
            Storyboard1.Begin();
            flag = true;
        }
        else
        {
            Button.Content = "Zoom In";
            Storyboard1_Rev.Begin();
            flag = false;
        }
    }

All you have to do is have a status flag that shows the current status of the element(s) that you wish to animate and animate it(them) in forward or reverse timeline as per the value of the flag.




回答2:


Instead of trying to set the Animation using that event, use a bool property to bind to instead:

<Image Name="ImageShowPanelTwo" Width="26" Height="26" Source="ImageRotate.png" 
    RenderTransformOrigin=".5,.5"  >
    <Image.RenderTransform>
        <RotateTransform x:Name="AnimatedRotateTransform" Angle="0" />
    </Image.RenderTransform>
    <Image.Style>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsRotated}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation 
                                    Storyboard.TargetName="AnimatedRotateTransform" 
                                    Storyboard.TargetProperty="Angle" 
                                    By="0"        
                                    To="90" 
                                    Duration="0:0:0.5" 
                                    AutoReverse="True"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsRotated}" Value="False">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation 
                                    Storyboard.TargetName="AnimatedRotateTransform" 
                                    Storyboard.TargetProperty="Angle" 
                                    By="90"        
                                    To="0" 
                                    Duration="0:0:0.5" 
                                    AutoReverse="True"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Image.Style>
</Image>

I trust that you can define your own bool property and invert it upon each MouseDown event occurrence to complete this functionality. As it is set to true the first Animation will start and as it is set to false the second will start.




回答3:


This is how I solved my problem:

Created four storyboards for the Stackpanel and the arrow:

<Storyboard x:Key="RotateIconUp">
            <DoubleAnimation Storyboard.TargetName="IconExpand" Storyboard.TargetProperty="(TextBlock.RenderTransform).(RotateTransform.Angle)" From="0" To="90" Duration="0:0:0.4" />
        </Storyboard>
        <Storyboard x:Key="RotateIconDown">
            <DoubleAnimation Storyboard.TargetName="IconExpand" Storyboard.TargetProperty="(TextBlock.RenderTransform).(RotateTransform.Angle)" From="90" To="0" Duration="0:0:0.4" />
        </Storyboard>
        <Storyboard x:Key="SlideGridDown">
            <DoubleAnimation Storyboard.TargetName="GridDetails" Storyboard.TargetProperty="(Grid.Height)" From="0" To="180" Duration="0:0:0.4" />
        </Storyboard>
        <Storyboard x:Key="SlideGridUp">
            <DoubleAnimation Storyboard.TargetName="GridDetails" Storyboard.TargetProperty="(Grid.Height)" From="180" To="0" Duration="0:0:0.4" />
        </Storyboard>

Then I trigger the storyboards from codebehind when the arrow is clicked:

private void ExpandDetails() {
        try {
            if (!pm_IsExanded) {
                Storyboard Storyboard = (Storyboard)FindResource("RotateIconUp");
                Storyboard.Begin(this);
                Storyboard = (Storyboard)FindResource("SlideGridDown");
                Storyboard.Begin(this);
                pm_IsExanded = true;
                BorderMain.BorderBrush = pm_BrushConverter.ConvertFromString("#000000") as Brush;
            } else {
                Storyboard Storyboard = (Storyboard)FindResource("RotateIconDown");
                Storyboard.Begin(this);
                Storyboard = (Storyboard)FindResource("SlideGridUp");
                Storyboard.Begin(this);
                pm_IsExanded = false;
                BorderMain.BorderBrush = pm_BrushConverter.ConvertFromString("#d0d0d0") as Brush;
            }
        } catch (Exception ee) {
            GlobalResource.WriteToLog("Error in ExpandDetails", ee);
        }
    }


来源:https://stackoverflow.com/questions/19376757/wpf-storyboard-same-trigger-but-reverse-behavior

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!