首页 > 解决方案 > WPF MVVM:从值变化触发行进点动画

问题描述

我正在尝试创建一个行进点动画,其中有一系列小圆圈,一个会变长一点并改变颜色,然后下一个会改变颜色,依此类推,这会来回反弹。动画将由 ViewModel 中的布尔值触发。

在决定使用 MVVM 模式之前,我实际上已经完成了这项工作,这很容易:我在 Page.Resources 标记中有故事板,并在按钮单击时从代码后面调用动画。现在我坚持这种解耦,似乎无论我尝试什么,总会出现某种错误。

这是xaml中的点:

<Grid Grid.Row="4" Grid.Column="3" Grid.ColumnSpan="2" Margin="20">
            <Grid.RowDefinitions>
                <RowDefinition Height="2*"/>
                <RowDefinition Height="1*"/>
                <RowDefinition Height="2*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>

            <Ellipse Name="Dot5" Grid.Row="1" Grid.Column="0" Style="{StaticResource ConnectionMeterOff}"/>
            <Ellipse Name="Dot4" Grid.Row="1" Grid.Column="1" Style="{StaticResource ConnectionMeterOff}"/>
            <Ellipse Name="Dot3" Grid.Row="1" Grid.Column="2" Style="{StaticResource ConnectionMeterOff}"/>
            <Ellipse Name="Dot2" Grid.Row="1" Grid.Column="3" Style="{StaticResource ConnectionMeterOff}"/>
            <Ellipse Name="Dot1" Grid.Row="1" Grid.Column="4" Style="{StaticResource ConnectionMeterOff}"/>

        </Grid>

还有我现在的动画:

<DataTemplate x:Key="MarchingDots">
  <DataTemplate.Triggers>
    <DataTrigger Binding="{Binding IsConnecting}" Value="true">
      <DataTrigger.EnterActions>
        <BeginStoryboard Name="MarchingDotsAnimation">
          <Storyboard RepeatBehavior="Forever" AutoReverse="True">
            <DoubleAnimation Storyboard.TargetName="Dot1" BeginTime="0:0:0.1"
                             Storyboard.TargetProperty="Width" From="10" To="15" 
                             Duration="0:0:0.2" AutoReverse="True"/>
            <ColorAnimation Storyboard.TargetName="Dot1" BeginTime="0:0:0.1"
                            Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"                   
                            From="#FF5B5B5B" To="#FF84161C" Duration="0:0:0.3" AutoReverse="True"/>

            <DoubleAnimation Storyboard.TargetName="Dot2" BeginTime="0:0:0.2" 
                             Storyboard.TargetProperty="Width" From="10" To="15" 
                             Duration="0:0:0.2" AutoReverse="True"/>
            <ColorAnimation Storyboard.TargetName="Dot2" BeginTime="0:0:0.2" 
                            Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)" 
                            From="#FF5B5B5B" To="#FF84161C" Duration="0:0:0.3" AutoReverse="True"/>

            <DoubleAnimation Storyboard.TargetName="Dot3" BeginTime="0:0:0.3" 
                             Storyboard.TargetProperty="Width" From="10" To="15" 
                             Duration="0:0:0.2" AutoReverse="True"/>
            <ColorAnimation Storyboard.TargetName="Dot3" BeginTime="0:0:0.3" 
                            Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)" 
                            From="#FF5B5B5B" To="#FF84161C" Duration="0:0:0.3" AutoReverse="True"/>

            <DoubleAnimation Storyboard.TargetName="Dot4" BeginTime="0:0:0.4" 
                             Storyboard.TargetProperty="Width" From="10" To="15" 
                             Duration="0:0:0.2" AutoReverse="True"/>
            <ColorAnimation Storyboard.TargetName="Dot4" BeginTime="0:0:0.4" 
                            Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)" 
                            From="#FF5B5B5B" To="#FF84161C" Duration="0:0:0.3" AutoReverse="True"/>

            <DoubleAnimation Storyboard.TargetName="Dot5" BeginTime="0:0:0.5" 
                             Storyboard.TargetProperty="Width" From="10" To="15" 
                             Duration="0:0:0.2" AutoReverse="True"/>
            <ColorAnimation Storyboard.TargetName="Dot5" BeginTime="0:0:0.5" 
                            Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)" 
                            From="#FF5B5B5B" To="#FF84161C" Duration="0:0:0.3" AutoReverse="True"/>
          </Storyboard>
        </BeginStoryboard>
      </DataTrigger.EnterActions>
    </DataTrigger>
  </DataTemplate.Triggers>
</DataTemplate>

我很确定我的问题不是绑定,而是动画没有附加到点上,或者我如何尝试包装它以访问触发器。如果我像以前一样保留它只是一个故事板,我没有任何东西可以触发它。如果我尝试一种风格,它会抱怨我可以使用“Storyboard.TargetName”。我也尝试过 EventTriggers。以及大量的谷歌搜索......所有示例似乎都是单个元素(如矩形),故事板可以附加到元素的样式。

我敢肯定这很简单,但我做错了什么?

标签: wpfxamlanimationmvvmdata-binding

解决方案


这需要一些清理,但它会让你开始。首先声明你的动画:

<Storyboard x:Key="Storyboard1" RepeatBehavior="Forever">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.25" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.25" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

<Storyboard x:Key="Storyboard2" RepeatBehavior="Forever">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.25" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.75" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.25" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.75" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

<Storyboard x:Key="Storyboard3" RepeatBehavior="Forever">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.75" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.75" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

<Storyboard x:Key="Storyboard4" RepeatBehavior="Forever">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.75" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.0" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.25" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:0.75" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.0" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.25" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

<Storyboard x:Key="Storyboard5" RepeatBehavior="Forever">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.25" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
        <EasingDoubleKeyFrame KeyTime="0:0:0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.0" Value="1"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.25" Value="1.15"/>
        <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

我将用椭圆构建它,因此为它们创建一个基本样式:

<Style x:Key="EllipseStyle" TargetType="Ellipse">
    <Setter Property="Fill" Value="Black" />
    <Setter Property="Width" Value="32" />
    <Setter Property="Height" Value="32" />
    <Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
    <Setter Property="Margin" Value="10" />
    <Setter Property="RenderTransform">
        <Setter.Value>
            <TransformGroup>
                <ScaleTransform/>
                <SkewTransform/>
                <RotateTransform/>
                <TranslateTransform/>
            </TransformGroup>
        </Setter.Value>
    </Setter>
</Style>

然后创建五个椭圆,为每个椭圆分配相应的故事板,并使用 DataTrigger 打开和关闭动画:

    <UniformGrid Columns="5">

        <Ellipse>
            <Ellipse.Style>
                <Style TargetType="Ellipse" BasedOn="{StaticResource EllipseStyle}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Animating}" Value="True">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Name="Storyboard1" Storyboard="{StaticResource Storyboard1}" />
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="Storyboard1" />
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Ellipse.Style>
        </Ellipse>

        <Ellipse>
            <Ellipse.Style>
                <Style TargetType="Ellipse" BasedOn="{StaticResource EllipseStyle}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Animating}" Value="True">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Name="Storyboard2" Storyboard="{StaticResource Storyboard2}" />
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="Storyboard2" />
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Ellipse.Style>
        </Ellipse>

        <Ellipse>
            <Ellipse.Style>
                <Style TargetType="Ellipse" BasedOn="{StaticResource EllipseStyle}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Animating}" Value="True">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Name="Storyboard3" Storyboard="{StaticResource Storyboard3}" />
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="Storyboard3" />
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Ellipse.Style>
        </Ellipse>

        <Ellipse>
            <Ellipse.Style>
                <Style TargetType="Ellipse" BasedOn="{StaticResource EllipseStyle}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Animating}" Value="True">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Name="Storyboard4" Storyboard="{StaticResource Storyboard4}" />
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="Storyboard4" />
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Ellipse.Style>
        </Ellipse>

        <Ellipse>
            <Ellipse.Style>
                <Style TargetType="Ellipse" BasedOn="{StaticResource EllipseStyle}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Animating}" Value="True">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Name="Storyboard5" Storyboard="{StaticResource Storyboard5}" />
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="Storyboard5" />
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Ellipse.Style>
        </Ellipse>

    </UniformGrid>

    <CheckBox Content="Animate" IsChecked="{Binding Animating}" HorizontalAlignment="Center"/>
</StackPanel>

我假设您的视图模型中有一个属性,称为Animating您用来控制它的属性,我还在此代码中添加了一个复选框以进行测试:

在此处输入图像描述

编辑:刚刚更新了动画以消除右侧的闪烁。


推荐阅读