2011-10-19 10 views
5

Sembra che abbia raggiunto una sorta di punto di rottura MVVM qui.Attivazione di un'animazione da un evento utilizzando MVVM

Mi piacerebbe che un controllo avesse la sua opacità animata per mezzo secondo (DoubleAnimation da 0.5 a 1.0) quando l'oggetto del modello di vista sottostante ha la proprietà "Status" modificata. L'ho raggiunto inizialmente utilizzando un DataTrigger ma poiché non ho trovato un modo per reagire a QUALSIASI modifica, solo un determinato valore, ho dovuto capovolgere sempre la proprietà "Status" degli oggetti VM su un valore speciale "in sospeso" prima di impostarlo al suo valore previsto. (? C'è un modo per reagire a qualsiasi cambiamento btw)

Questo è stato hacky così ho iniziato a giocherellare con Eventtriggers invece ...

questo è quello che ho provato finora:

  1. utilizzando un normale EventTrigger

Questo sembra richiedere un RoutedEvent ma che, a sua volta, richiede che il mio sottostante sottofinestra modello eredita da DependencyObject.

  1. Utilizzando i:Interaction.Triggers

In questo modo posso ascoltare e reagire a normali eventi NET, ma non ho trovato un modo per iniziare un StoryBoard utilizzando questo approccio.

  1. Utilizzando i:Interaction.Triggers e la scrittura di un Behavior

Questo esperimento sono venute meno sul fatto che ho trovato alcun modo per collegare il mio comportamento personalizzato al suo controllo associato.

Questo è ciò che il codice XAML sembrava:

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

Ed ecco il comportamento personalizzato:

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

che non ha funzionato perché il parser XAML imponeva di essere collegati direttamente a "MyControl "ma ho bisogno di collegarlo al trigger dell'evento. Allora ho provato questo approccio:

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

questo non è riuscito sul fatto che lookupVisualParent non funziona. Il genitore logico del comportamento è sempre null.

Mi sembra che questo dovrebbe essere un compito abbastanza comune? C'è una buona soluzione a questo problema? Trovo strano che scriverò le mie classi di modelli di viste in modo che esse deriveranno da DependencyObject per avviare un'animazione quando si verifica un evento.

Acclamazioni

risposta

4

Si potrebbe semplicemente usare una bandiera: impostare una bandiera sul vostro VM chiama 'RecentlyChangedFlag'. Pulirlo a true, quindi a false, ogni volta che i valori appropriati cambiano. Puoi fare così:

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 il codice sopra impostato ChangedFlag = true quando si desidera segnalare l'animazione per l'avvio. Verrà ripristinato su false dopo che WPF interroga il valore vero.

Quindi l'animazione si verifica quando il valore di RecentlyChangedFlag è true, ad esempio EnterAction.

Spero che questo aiuti.

+0

Grazie, ma è così che inizialmente ho "risolto" (utilizzando un DataTrigger) ma, come ho sottolineato nell'OP, è hacky. Sono abbastanza nuovo per WPF quindi potrebbe essere questo è in realtà il modo normale di attivare comportamenti/animazioni dalla VM ma a me sembra che stiano lavorando attorno a una limitazione che non dovrebbe esserci. Potrebbe essere che semplicemente non puoi attivare le animazioni dagli eventi nella VM? –

+0

Esiste un ViewModel per tradurre un modello in qualcosa che una vista può usare. In quanto tale è il posto perfetto per fare cose come questa. Non è che l'IMO hacky e una bandiera che indica che qualcosa di importante è cambiato dovrebbero esistere nella VM. Fintanto che ViewModel si alza da solo, non è un problema. Un ViewModel con una bandiera per indicare qualunque "evento" di cui parli - se lo chiami la cosa giusta, non sembra troppo fuori luogo? –

+1

Non ho intenzione di essere religioso riguardo a questo, ma sembra strano che sia impossibile attivare animazioni dagli eventi. Dopo tutto, gli eventi sono lì per segnalare cambiamenti di stato. Dovendo aggiungere una seconda proprietà solo per segnalare le modifiche dello stato al primo è una soluzione IMHO. Sono sorpreso di vedere che gli strumenti messi a disposizione da WPF sono così carenti. Posso certamente vivere con la "soluzione di proprietà statale" in questo caso particolare, ma immagino che le mie classi VM saranno piene di tali proprietà in futuro. Ci deve essere un modo migliore, giusto? –