WPF故事板 - 相同触发器,但相反的行为

4
我有一个名为Stackpanel One的控件,其包含一些内容、一个图像和一个默认隐藏的SubStackpanel。当点击图像时,图像应该旋转90度并向下滑动SubStackpanel。 再次点击图像时,图像应旋转回原始位置,并且SubStackpanel应上滑到默认隐藏位置。
我几乎实现了这个功能,但问题是我不知道如何在两个不同的Storyboard动画中使用相同的触发事件。所以现在每次单击图像时,只会发生按钮和SubStackpanel的第一个动画。 我尝试过AutoReverse属性,但它会在动画完成后立即触发。这当然只应在用户第二次单击图像时发生。
我希望只使用标记就能实现这一点。
这是我的当前代码:
<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 :)

3个回答

4

使用Blend for Visual Studio可以更好、更简单地完成这个操作,而不必处理大量代码。

以下是完成此操作的步骤列表:

步骤1:同时在Blend和Visual Studio中打开项目。

步骤2:在Blend中,为您希望使用动画效果的所有元素创建一个新的Storyboard。我们称之为“Storyboard1”。

Create a storyboard

步骤3:现在,打开刚刚创建的Storyboard,并单击“+”旁边的小箭头,然后在下拉菜单中单击“Duplicate”。然后,将创建一个名为“Storyboard1_Copy”的新Storyboard。

Duplicating a storyboard

步骤4:将这个新的Storyboard重命名为您喜欢的名称,比如“Storyboard1_Rev”。

Renaming the newly created storyboard

Storyboard with new name

步骤5:您现在应该已经猜到了。选择复制的Storyboard,并从下拉菜单中单击“Reverse”。

Reversing the storyboard

步骤6:现在您已经准备好了两个Storyboard:一个用于按照您的喜好对某些元素进行动画处理,另一个用于反转该动画序列。就像从C#代码中调用Storyboard一样,您可以从同样的代码中调用反转后的Storyboard,前提是要检查元素是否已经被动画处理过。为此,我使用一个bool变量,每次对一组元素进行动画处理时都会更改其值(即如果元素尚未被动画处理,则为false,如果已经被处理,则为true)。

以下是示例说明:

我将创建一个具有简单布局的应用程序。它在屏幕上有一个按钮、一个图像和一个矩形区域。

Initial screen layout

想法是,每当您点击按钮一次时,图像应该最大化,并在第二次点击按钮时恢复到原始大小,以此类推。也就是说,每隔一次单击按钮就会发生反向动画。以下是一些截图,展示了它是如何实现的:

您可以看到按钮显示的是图像的当前状态。当图像处于初始小尺寸时,它显示“放大”,而当图像最大化时,则显示“缩小”。
最后,以下是处理按钮点击的C#代码:

Initial Zooming in Zoomed in Zooming out Zoomed out

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

你只需要拥有一个状态标志,显示您希望动画的元素的当前状态,并根据标志的值在正向或反向时间轴上对其进行动画处理。


非常感谢您的努力! - Farsen

1

不要尝试在事件中设置 Animation,而是使用一个 bool 属性进行绑定:

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

我相信你可以定义自己的bool属性,并在每次MouseDown事件发生时反转它,以完成此功能。因为它被设置为true,所以第一个Animation将会启动,而当它被设置为false时,第二个将会启动。


啊...我们有两个问题...第一个问题是RotateTransform类没有Style属性来移动该动画,如果您无法定义一个bool属性,那么这种方法根本行不通。 - Sheridan
我敢说在WPF中几乎任何事情都是可能的,但是对于您的要求,不幸的是这种方法行不通。 - Sheridan
好的。 但是如果我接受需要在代码后台进行一些工作,你会建议什么是解决这个问题的最佳方案呢? - Farsen
1
这是一个棘手的问题...通常当我们遇到关于“TargetName”属性的错误时,它只意味着我们需要将动画代码移动到该对象,但在这种情况下我们无法这样做,因为“RotateTransform”没有“Style”属性。此外,在普通的“Triggers”集合中,我们不能使用“DataTemplate”。我能想到的唯一解决方法就是让您在代码中完成所有的动画。 - Sheridan
我知道了。无论如何,感谢你的时间和努力! :) - Farsen
显示剩余2条评论

1
这是我解决问题的方法:
为Stackpanel和箭头创建了四个故事板:
<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>

当箭头被点击时,我从代码后台触发故事板:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接