WPF MVVM:从值更改触发行进点动画
我正在尝试创建一个行进点动画,其中有一系列的小圆圈,其中一个会稍微变大并改变颜色,然后下一个会变大,以此类推,这会来回反弹。动画将由ViewModel中的布尔值触发 实际上,在决定使用MVVM模式之前,我已经做了这项工作,这很简单:我在页面中有故事板。参考资料标签,并在点击按钮时从代码后面调用动画。现在我被这个解耦的问题困住了,似乎无论我尝试什么,总会有某种错误 以下是xaml中的点: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="
<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断开连接来触发它,所以如果我们能够以同样的方式触发动画的可见性,为什么他们会让开始/停止变得棘手?而且,我担心它一直在运行,我们只是在消耗我们不需要的资源。非常感谢您的时间标记!这很有魅力。在执行这个过程中有很多细微之处,但是由于您的详细描述,它确实很容易理解。唯一不同的是,我把第一帧和最后一帧切成两半,这样它们在两端都不会反弹,但是,你做到了!