当ViewModel中的属性更新时,我一直在试图弄清楚如何在View中有效地触发动画,其中动画取决于所述属性的值。
我在一个简单的应用程序中使用单个View和ViewModel重新创建了我的问题。这里的目标是使用ColorAnimation转换矩形的颜色变化。作为参考,我一直在使用Josh Smith的MVVM Foundation包。
示例项目可以是downloaded here.
总而言之,我想在Color属性发生变化时在View中设置颜色过渡动画。
MainWindow.xaml
<Window x:Class="MVVM.ColorAnimation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ColorAnimation="clr-namespace:MVVM.ColorAnimation" Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<ColorAnimation:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Rectangle Margin="10">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Color}"/>
</Rectangle.Fill>
</Rectangle>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<Button Command="{Binding BlueCommand}" Width="100">Blue</Button>
<Button Command="{Binding GreenCommand}" Width="100">Green</Button>
</StackPanel>
</Grid>
</Window>
MainWindowViewModel.cs
namespace MVVM.ColorAnimation
{
using System.Windows.Input;
using System.Windows.Media;
using MVVM;
public class MainWindowViewModel : ObservableObject
{
private ICommand blueCommand;
private ICommand greenCommand;
public ICommand BlueCommand
{
get
{
return this.blueCommand ?? (this.blueCommand = new RelayCommand(this.TurnBlue));
}
}
private void TurnBlue()
{
this.Color = Colors.Blue;
}
public ICommand GreenCommand
{
get
{
return this.greenCommand ?? (this.greenCommand = new RelayCommand(this.TurnGreen));
}
}
private void TurnGreen()
{
this.Color = Colors.Green;
}
private Color color = Colors.Red;
public Color Color
{
get
{
return this.color;
}
set
{
this.color = value;
RaisePropertyChanged("Color");
}
}
}
}
无论如何从View中触发ColorAnimation而不是值之间的即时转换?我现在这样做的方式是另一个应用程序非常混乱,因为我通过ViewModel
属性设置ViewModel,然后使用PropertyObserver监视值更改,然后创建动画并从代码隐藏中触发它:
this.colorObserver = new PropertyObserver<Player>(value)
.RegisterHandler(n => n.Color, this.CreateColorAnimation);
在我处理多种颜色和许多潜在动画的情况下,这变得非常混乱,并且弄乱了我手动将ViewModel传递给View而不是简单地通过ResourceDictionary绑定两者的事实。 。我想我也可以在DataContextChanged
事件中做到这一点,但是有更好的方法吗?
答案 0 :(得分:5)
如果只是为了几个动画,我建议使用Visual States。然后,您可以在视图上使用GoToAction行为来触发不同的动画。如果您正在处理许多类似的动画,那么创建自己的行为将是一个更好的解决方案。
<强>更新强> 我已经创建了一个非常简单的行为来给Rectangle一个小的颜色动画。这是代码。
public class ColorAnimationBehavior : TriggerAction<FrameworkElement>
{
#region Fill color
[Description("The background color of the rectangle")]
public Color FillColor
{
get { return (Color)GetValue(FillColorProperty); }
set { SetValue(FillColorProperty, value); }
}
public static readonly DependencyProperty FillColorProperty =
DependencyProperty.Register("FillColor", typeof(Color), typeof(ColorAnimationBehavior), null);
#endregion
protected override void Invoke(object parameter)
{
var rect = (Rectangle)AssociatedObject;
var sb = new Storyboard();
sb.Children.Add(CreateVisibilityAnimation(rect, new Duration(new TimeSpan(0, 0, 1)), FillColor));
sb.Begin();
}
private static ColorAnimationUsingKeyFrames CreateVisibilityAnimation(DependencyObject element, Duration duration, Color color)
{
var animation = new ColorAnimationUsingKeyFrames();
animation.KeyFrames.Add(new SplineColorKeyFrame { KeyTime = new TimeSpan(duration.TimeSpan.Ticks), Value = color });
Storyboard.SetTargetProperty(animation, new PropertyPath("(Shape.Fill).(SolidColorBrush.Color)"));
Storyboard.SetTarget(animation, element);
return animation;
}
}
在xaml中,您只需附加此行为,
<Rectangle x:Name="rectangle" Fill="Black" Margin="203,103,217,227" Stroke="Black">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<local:ColorAnimationBehavior FillColor="Red"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
单击矩形时,它应从黑色变为红色。
答案 1 :(得分:4)
我使用了Xin发布的代码,并做了一些非常小的调整(代码如下)。唯一的3个不同之处:
我创建了适用于任何UIElement的行为,而不仅仅是一个矩形
我使用了PropertyChangedTrigger而不是EventTrigger。让我来监视ViewModel上的color属性,而不是监听点击事件。
我将FillColor绑定到ViewModel的Color属性。
要使用它,您需要下载Blend 4 SDK(它是免费的,如果您还没有Expression Blend,则只需要它),并添加对System.Windows.Interactivity和Microsoft.Expression的引用.Interactions
以下是行为类的代码:
// complete code for the animation behavior
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace ColorAnimationBehavior
{
public class ColorAnimationBehavior: TriggerAction<UIElement>
{
public Color FillColor
{
get { return (Color)GetValue(FillColorProperty); }
set { SetValue(FillColorProperty, value); }
}
public static readonly DependencyProperty FillColorProperty =
DependencyProperty.Register("FillColor", typeof(Color), typeof(ColorAnimationBehavior), null);
public Duration Duration
{
get { return (Duration)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
// Using a DependencyProperty as the backing store for Duration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(Duration), typeof(ColorAnimationBehavior), null);
protected override void Invoke(object parameter)
{
var storyboard = new Storyboard();
storyboard.Children.Add(CreateColorAnimation(this.AssociatedObject, this.Duration, this.FillColor));
storyboard.Begin();
}
private static ColorAnimationUsingKeyFrames CreateColorAnimation(UIElement element, Duration duration, Color color)
{
var animation = new ColorAnimationUsingKeyFrames();
animation.KeyFrames.Add(new SplineColorKeyFrame() { KeyTime = duration.TimeSpan, Value = color });
Storyboard.SetTargetProperty(animation, new PropertyPath("(Shape.Fill).(SolidColorBrush.Color)"));
Storyboard.SetTarget(animation, element);
return animation;
}
}
}
现在,这是将它连接到矩形的XAML:
<UserControl x:Class="MVVM.ColorAnimation.Silverlight.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ColorAnimation="clr-namespace:MVVM.ColorAnimation"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ca="clr-namespace:ColorAnimationBehavior;assembly=ColorAnimationBehavior"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.DataContext>
<ColorAnimation:MainWindowViewModel />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Rectangle x:Name="rectangle" Margin="10" Stroke="Black" Fill="Red">
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding Color}">
<ca:ColorAnimationBehavior FillColor="{Binding Color}" Duration="0:0:0.5" />
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
</Rectangle>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<Button Command="{Binding BlueCommand}" Width="100" Content="Blue"/>
<Button Command="{Binding GreenCommand}" Width="100" Content="Green"/>
</StackPanel>
</Grid>
</UserControl>
这真的是辛的想法 - 我只是把它清理了一下。