2011-05-08 88 views
6

我一直在试图弄清楚如何在视图模型更新时动态取决于所述属性的值的情况下如何有效地触发视图中的动画。使用MVVM的动态视图动画

我用一个View和ViewModel在一个简单的应用程序中重新创建了我的问题。这里的目标是通过使用ColorAnimation来转换矩形的颜色变化。作为参考,我一直在使用Josh Smith的MVVM Foundation包。

示例项目可以downloaded here.

总之,我想动画在视图每当颜色属性更改颜色过渡。

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"); 
      } 
     }  
    } 
} 

反正从视图触发ColorAnimation而不是值之间的即时转变?我目前这样做的方式是另一个应用程序非常混乱,因为我通过ViewModel属性设置ViewModel,然后使用PropertyObserver监视值更改,然后创建动画并从代码隐藏中触发它:

this.colorObserver = new PropertyObserver<Player>(value) 
    .RegisterHandler(n => n.Color, this.CreateColorAnimation); 

在我处理很多颜色和许多潜在动画的情况下,这会变得非常混乱,并且混淆了我手动将ViewModel传递给View的事实,而不是简单地通过ResourceDictionary中。我想我也可以在DataContextChanged事件中做到这一点,但是有没有更好的方法?

+0

该项目的链接不再有效。 – itsho 2013-11-12 09:12:08

回答

5

如果只是为了一些动画,我会建议使用视觉状态。然后,您可以在视图上使用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> 

当您单击矩形,它应该从黑色的颜色去红。

+0

你是否有一个简单的例子,最好根据上面的例子?我一直试图按照你指出的方向查找事情,但还没有弄明白。按原样,我假设Color对象可以是单独的视图模型的任何颜色。 – 2011-05-11 01:01:37

+0

嗨,请看我更新的答案。 :) – 2011-05-11 13:46:30

+0

我不完全确定这是我的意思,并且找不到将其应用于我的特定情况的方法。在这种情况下,彩色动画完全不关注视图模型,并且取决于事件。通常情况下,该事件将在视图模型中更改颜色属性。 – 2011-05-15 05:39:10

4

我用了张贴的代码,并做了一些非常小的推特(代码如下)。唯一的3重大差异:

我创建的行为在任何的UIElement工作,不只是一个矩形

我用PropertyChangedTrigger,而不是一个EventTrigger。让我来监视ViewModel上的颜色属性,而不是监听点击事件。

我将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> 

 

它真的是辛的想法 - 我只是把它清理了一下。