2011-05-08 16 views
6

He estado tratando de descubrir cómo activar de manera efectiva las animaciones en una vista cuando se actualiza una propiedad en ViewModel, donde la animación depende del valor de dicha propiedad.Animaciones de vista dinámica usando MVVM

He recreado mi problema en una aplicación simple con una sola Vista y un Modelo de Vista. El objetivo aquí es hacer la transición del cambio de color de un rectángulo utilizando una Animación de color. Como referencia, he estado usando el paquete MVVM Foundation de Josh Smith.

El proyecto de ejemplo puede ser downloaded here.

En resumen, quiero animar la transición de color en la vista siempre que cambia la propiedad de color.

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

¿Hay alguna forma de la vista para desencadenar una ColorAnimation en lugar de una transición instantánea entre los valores? La forma en que estoy haciendo esto actualmente es otra aplicación bastante desordenada, en la que establezco ViewModel a través de una propiedad ViewModel, y luego uso un PropertyObserver para supervisar los cambios de valores, luego creo la animación y la desencadeno desde el código subyacente:

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

En una situación en la que estoy lidiando con muchos colores y muchas animaciones potenciales, esto se convierte en un desastre, y arruina el hecho de que estoy pasando manualmente el ViewModel a la Vista, simplemente vinculando los dos a través de un ResourceDictionary. Supongo que podría hacer esto en el evento DataContextChanged también, pero ¿hay una mejor manera?

+0

El enlace al proyecto ya no es válido. – itsho

Respuesta

5

Si solo por unas pocas animaciones recomiendo usar Visual States. Luego puede usar el comportamiento de GoToAction en la vista para activar diferentes animaciones. Si está tratando con muchas animaciones similares, crear su propio comportamiento sería una mejor solución.

actualización He creado una behaivor muy simple para dar un rectángulo un poco de animación de color. Aquí está el código.

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

    } 

en XAML, sólo tiene que adjuntar este comportamiento como éste,

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

Al hacer clic en el rectángulo, que debe ir desde el color Negro a rojo.

+0

¿Tiene un ejemplo simple, preferiblemente en términos del ejemplo anterior? He estado tratando de buscar las cosas en la dirección que has señalado pero aún no las he descifrado. Tal como está, supongo que el objeto Color podría ser de cualquier color por un modelo de vista separado. –

+0

Hola, por favor mira mi respuesta actualizada. :) –

+0

No estoy del todo seguro de que esto es lo que quise decir, y no puedo encontrar una manera de aplicarlo a mi caso específico. En este caso, la animación de color no está prestando atención al modelo de vista en absoluto, y depende de un evento. Normalmente, el evento sería que la propiedad Color cambió en el modelo de vista. –

4

Utilicé el código que Xin publicó e hice algunos tweeks muy pequeños (el código está debajo). Las únicas diferencias 3 materiales:

que crearon el comportamiento para trabajar en cualquier UIElement, no sólo un rectángulo

he usado un PropertyChangedTrigger en lugar de un EventTrigger. Eso me permite supervisar la propiedad de color en ViewModel en lugar de escuchar eventos de clic.

Adjunté FillColor a la propiedad Color del ViewModel.

Para usar esto, deberá descargar Blend 4 SDK (es gratis, y solo lo necesita si aún no tiene Expression Blend) y agregar referencias a System.Windows.Interactivity y Microsoft. Expression.Interactions

aquí está el código para la clase de comportamiento:

 

// 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; 
     } 
    } 
} 

 

Ahora aquí está el XAML que se engancha en manos de su rectángulo:

 

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

 

se fue realmente la idea de Xin. Lo limpie un poco.

Cuestiones relacionadas