WPF MVVM:从值更改触发行进点动画

WPF MVVM:从值更改触发行进点动画,wpf,xaml,animation,mvvm,data-binding,Wpf,Xaml,Animation,Mvvm,Data Binding,我正在尝试创建一个行进点动画,其中有一系列的小圆圈,其中一个会稍微变大并改变颜色,然后下一个会变大,以此类推,这会来回反弹。动画将由ViewModel中的布尔值触发 实际上,在决定使用MVVM模式之前,我已经做了这项工作,这很简单:我在页面中有故事板。参考资料标签,并在点击按钮时从代码后面调用动画。现在我被这个解耦的问题困住了,似乎无论我尝试什么,总会有某种错误 以下是xaml中的点: <Grid Grid.Row="4" Grid.Column="3" Grid.ColumnSpan="

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

实际上,在决定使用MVVM模式之前,我已经做了这项工作,这很简单:我在页面中有故事板。参考资料标签,并在点击按钮时从代码后面调用动画。现在我被这个解耦的问题困住了,似乎无论我尝试什么,总会有某种错误

以下是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。我也尝试过事件触发器。还有大量的谷歌搜索……所有的例子似乎都是单个元素,比如一个矩形,故事板可以附着到元素的样式上


我相信这很简单,但我做错了什么?

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

<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的属性,您可以使用该属性来控制此操作,我还为此代码添加了一个复选框以进行测试:


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

使用EventTrigger有什么特殊原因吗?如果你用一个来启动模拟,那么你需要另一个来停止它。这里通常的解决方案是制作一个循环动画,然后用一个布尔标志来控制它的可见性,这对你有用吗?@MarkFeldman:我想把这些圆点显示为USB电缆的可视表示,然后当按下连接按钮时,圆点将首先离开主机,然后再返回。我试图通过viewModel中的bool断开连接来触发它,所以如果我们能够以同样的方式触发动画的可见性,为什么他们会让开始/停止变得棘手?而且,我担心它一直在运行,我们只是在消耗我们不需要的资源。非常感谢您的时间标记!这很有魅力。在执行这个过程中有很多细微之处,但是由于您的详细描述,它确实很容易理解。唯一不同的是,我把第一帧和最后一帧切成两半,这样它们在两端都不会反弹,但是,你做到了!