如何根据控件的属性(Top / Tabstop)设置WPF动画BeginTime

时间:2011-07-15 23:05:14

标签: wpf xaml animation expression-blend

我有一个WPF应用程序,它在垂直堆栈面板中有一系列按钮,类似于DVR菜单的样子。我想出了如何制作一个动画,当每个新的菜单页面被加载时,按钮/控件全部{unfade,fall,deblur,whatever}成为现实,这很好但有点过于统一。我真正喜欢的是根据每个按钮的位置或tabstop属性,在每个按钮上启动动画。因此,例如顶部按钮将是第一个开始,然后是下一个等,直到底部按钮,可能在每次启动之间延迟50毫秒(但是这样第一个按钮不必在第二个按钮之前完成)开始)。当然,我可以为每个按钮制作不同的动画,但我希望有一个更优雅的解决方案。我没有看到将任何控件的属性加载到故事板的BeginTime中的方法。有没有一种很好的方法只使用XAML,或者像这样的东西需要代码隐藏?如果是后者,是否可以将其打包成一个行为,并能够在以后以声明方式加载,或者我是否永远坚持使用命令式代码?

1 个答案:

答案 0 :(得分:8)

简短的回答是否定的(据我所知),你不能完全你只想用XAML,这就是原因。

虽然在WPF中有很多方法可以制作动画,但使用XAML制作动画的唯一方法是使用Storyboard。跨元素共享Storyboard的唯一方法是将其打包在诸如Style,ControlTemplate或DataTemplate之类的资源中。但是,为了将Storyboard用作资源,它必须是Freezable,这意味着它不能包含将动画BeginTime与动画目标的属性(例如TabIndex或某些属性)相关联所需的任何数据绑定表达式你的按钮的其他属性)。即使你能以某种方式将动画的BeginTime绑定到Button的属性,你仍然需要使用codebehind来编写ValueConverter来将Button属性值转换为TimeSpan值(你想要的50ms累积延迟)。

因此,为了使完全你想要的东西,你需要使用代码隐藏,如下例所示:

XAML:

<StackPanel HorizontalAlignment="Left" Width="100" Loaded="StackPanel_Loaded">
    <Button Content="Button1"/>
    <Button Content="Button2"/>
    <Button Content="Button3"/>
    <Button Content="Button4"/>
    <Button Content="Button5"/>
    <Button Content="Button6"/>
    <Button Content="Button7"/>
    <Button Content="Button8"/>
    <Button Content="Button9"/>
    <Button Content="Button10"/>
</StackPanel>

代码隐藏:

private void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
    StackPanel stackPanel = sender as StackPanel;
    DoubleAnimation fadeInAnimation = new DoubleAnimation(1.0, new Duration(TimeSpan.FromMilliseconds(200)));
    for (int i = 0; i < stackPanel.Children.Count; i++)
    {
        fadeInAnimation.BeginTime = TimeSpan.FromMilliseconds(i * 50);
        stackPanel.Children[i].Opacity = 0.0;
        stackPanel.Children[i].BeginAnimation(UIElement.OpacityProperty, (AnimationTimeline)fadeInAnimation.GetAsFrozen());
    }
}

话虽如此,有一些方法可以仅使用XAML来估算您想要的行为。例如,您可以使用LinearGradientBrush OpacityMask覆盖StackPanel,并使用单个Storyboard为其设置动画,如下所示:

XAML:

<StackPanel HorizontalAlignment="Left" Width="100">
    <StackPanel.OpacityMask>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="Black"/>
            <GradientStop x:Name="TransparentGradient" Color="Transparent"/>
        </LinearGradientBrush>
    </StackPanel.OpacityMask>
    <StackPanel.Triggers>
        <EventTrigger RoutedEvent="Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation
                        Storyboard.TargetName="TransparentGradient" Storyboard.TargetProperty="Offset"
                        To="1.0" Duration="0:0:0.5"/>
                    <ColorAnimation
                        Storyboard.TargetName="TransparentGradient" Storyboard.TargetProperty="Color"
                        To="Black" BeginTime="0:0:0.5" Duration="0:0:0.5"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </StackPanel.Triggers>
    <Button Content="Button1"/>
    <Button Content="Button2"/>
    <Button Content="Button3"/>
    <Button Content="Button4"/>
    <Button Content="Button5"/>
    <Button Content="Button6"/>
    <Button Content="Button7"/>
    <Button Content="Button8"/>
    <Button Content="Button9"/>
    <Button Content="Button10"/>
</StackPanel>

这会给你一个很好的从上到下淡入。其他效果也可以通过一点点创造力,但一个建议:尽量避免花费太多时间找到复杂的XAML解决方案,当你可以达到预期的效果只需几行代码。

快乐的编码!