2011-10-19 11 views
5

Parece que he llegado a algún tipo de punto de ruptura de MVVM aquí.Activación de una animación de un evento utilizando MVVM

Me gustaría que un control tenga su opacidad animada durante medio segundo (DoubleAnimation de 0.5 a 1.0) cuando el objeto del modelo de vista subyacente tenga su propiedad de "Estado" modificada. Lo logré al principio usando un DataTrigger pero como no he encontrado una forma de reaccionar a CUALQUIER cambio, solo un valor dado, tuve que voltear siempre la propiedad "Status" de los objetos de VM hasta un valor "pendiente" especial antes de configurarlo a su valor previsto. (? ¿Hay una manera de reaccionar ante cualquier cambio por cierto)

Ésta era hacky así que empecé a tocar el violín con Eventtriggers lugar ...

Esto es lo que he probado hasta ahora:

  1. el uso de una normal de EventTrigger

Esto parece requerir un RoutedEvent pero que, a su vez, requiere que mi vista del modelo subyacente objeto hereda de DependencyObject.

  1. Usando i:Interaction.Triggers

De esa manera puedo escuchar y reaccionar a los eventos de .NET normales pero no he encontrado una manera de empezar un StoryBoard utilizando este enfoque.

  1. Usando i:Interaction.Triggers y escribir un Behavior

Este experimento se quedaron cortos en el hecho no he encontrado ninguna manera de unir mi comportamiento personalizado para su control asociado.

Esto es lo que el XAML parecía:

<cc:MyControl> 
     <i:Interaction.Triggers> 
      <i:EventTrigger EventName="Updated"> 
       <i:Interaction.Behaviors> 
        <cv:OpacityBehavior Duration="0:0:0:5" /> 
       </i:Interaction.Behaviors> 
      </i:EventTrigger> 
     </i:Interaction.Triggers> 

Y aquí es el comportamiento personalizado:

class OpacityBehavior : Behavior<MyControl> 
{ 
    public Duration Duration { get; set; } 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 
     var animation = new DoubleAnimation(0.5, 1, Duration, FillBehavior.HoldEnd); 
     var associatedObject = lookupVisualParent(this); 
     associatedObject.BeginAnimation(UIElement.OpacityProperty, animation); 
    } 
} 

que no funcionó porque el analizador XAML necesario que se unan directamente a "MyControl "pero necesito adjuntarlo al desencadenador del evento". Luego trató este enfoque:

class OpacityBehavior : Behavior<DependencyObject> 
{ 
    public Duration Duration { get; set; } 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 
     var animation = new DoubleAnimation(0.5, 1, Duration, FillBehavior.HoldEnd); 
     var associatedObject = lookupVisualParent(this); 
     associatedObject.BeginAnimation(UIElement.OpacityProperty, animation); 
    } 

    private UIElement lookupVisualParent(DependencyObject dObj) 
    { 
     if (dObj is UIElement) 
      return (UIElement) dObj; 

     if (dObj == null) 
      return null; 

     return lookupVisualParent(LogicalTreeHelper.GetParent(dObj)); 
    } 
} 

Esto no se ajustó en el hecho de que lookupVisualParent no funciona. El padre lógico del comportamiento siempre es null.

Me parece que esto debería ser una tarea bastante común? ¿Hay una buena solución a este problema? Me resulta extraño que tenga que escribir las clases de mi modelo de vista para que se derivan de DependencyObject con el fin de iniciar una animación cuando se desencadena un evento.

Saludos

Respuesta

4

Usted simplemente puede usar una bandera: establecer un indicador en su máquina virtual llamada 'RecentlyChangedFlag'. Pulsarlo a true, luego false, siempre que cambien los valores apropiados. Puede hacerlo así:

private bool _changedFlag; 
public bool ChangedFlag 
{ 
    get 
    { 
     if (_changedFlag) 
     { 
      _changedFlag = false; 
      OnPropertyChanged("ChangedFlag"); 
      return true; 
     } 
     // (else...) 
     return false; 
    } 
    protected set 
    { 
     _changedFlag = value; 
     OnPropertyChanged("ChangedFlag"); 
    } 
} 

I.e., con el código anterior establecido en ChangedFlag = true cuando desee indicar la animación para comenzar. Se restablecerá a falso después de que WPF consulte el valor verdadero.

Luego haga que la animación se produzca cuando el valor de RecentlyChangedFlag es , por ejemplo, EnterAction.

Espero que ayude.

+0

Gracias, pero así es como originalmente lo "resolví" (usando un DataTrigger) pero, como señalé en el OP, es cursi. Soy bastante nuevo en WPF, por lo que podría ser que esta es la manera normal de activar conductas/animaciones desde la máquina virtual, pero a mí esto parece estar solucionando una limitación que no debería estar ahí. ¿Podría ser que simplemente no puede activar animaciones de eventos en la máquina virtual? –

+0

Existe ViewModel para convertir un modelo en algo que una vista puede usar. Como tal, es el lugar perfecto para hacer cosas como esta. No es esa IMO hacky y una bandera que indica que algo importante ha cambiado debería existir en la máquina virtual. Siempre que ViewModel se levante por sí solo, no es un problema. Un modelo de vista que tiene una bandera para indicar de qué "evento" está hablando. Si lo llama correcto, ¿no parece demasiado fuera de lugar? –

+1

No voy a ser religioso al respecto, pero me parece extraño que sea imposible activar animaciones a partir de eventos. Después de todo, los eventos están ahí para señalar los cambios de estado. Tener que agregar una segunda propiedad solo para señalar cambios de estado a la primera es una solución alternativa en mi humilde opinión. Me sorprende ver que las herramientas disponibles por WPF son deficientes. Ciertamente puedo vivir con la "solución de propiedad estatal" en este caso particular, pero imagino que mis clases de VM estarán llenas de tales propiedades en el futuro. Tiene que haber una mejor manera, ¿verdad? –

Cuestiones relacionadas