2012-11-26 20 views
14

Quindi ho una semplice configurazione, una autocompletebox con il suo evento Popolamento che voglio associare a un comando. Io usoCome posso passare l'argomento dell'evento a un comando utilizzando i trigger?

clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity 

(c'è uno spazio dei nomi migliori per fare questo?)

Non è un grosso problema per legarlo, il grosso problema è quello di passare che PopulatingEventArgs argomento del comando associato.

Quindi, come posso farlo secondo le migliori pratiche di PRISM in particolare e MVVM in generale?

+0

Non inserire tag nel titolo della domanda. Vedi [questo link] (http://meta.stackexchange.com/questions/10647/how-do-i-write-a-good-title) per scrivere buoni titoli;) –

risposta

24

Non c'è modo integrato, ecco come lo faccio:

Il trigger Interaction classico è usato in questo modo:

<Button Content="I am a button"> 
    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="MouseEnter"> 
      <i:InvokeCommandAction Command="{Binding CommandWithNoArgs}" /> 
     </i:EventTrigger> 
    </i:Interaction.Triggers> 
</Button> 

Non possiamo accedere al EventArgs del MouseEnter evento attraverso l'associazione, quindi dovremo modificare il pezzo che lo getta via.
In questo caso, il pezzo è il InvokeCommandAction.

"Quindi andiamo a suddividerla e ignorare un metodo conveniente che ci aspettava da sempre" è quello che mi sarebbe piaciuto scrivere. Ma la classe è sigillata.

così stiamo andando ad avere per creare una sottoclasse suo genitore (abstract) classe: TriggerAction<DependencyObject>

L'implementazione di base è:

public class InteractiveCommand : TriggerAction<DependencyObject> 
{ 
    protected override void Invoke(object parameter) 
    { 
    } 
} 

E questo è il vostro parameterEventArgs!
Ma resisti, non è così semplice, dobbiamo riprodurre il comportamento di un normale InvokeCommandAction.
Tramite Reflector, l'ho decompilato (ma potresti andare a dare un'occhiata alla fonte ufficiale, sono solo pigro).

Non ci occuperemo della proprietà di dipendenza CommandParameter, supponiamo che se si utilizza questa invece di InvokeCommandAction, in realtà si desidera il EventArgs ogni volta.

Qui vale la classe completo (WPF solo, vedi EDIT per Silverlight):

public class InteractiveCommand : TriggerAction<DependencyObject> 
{ 
    protected override void Invoke(object parameter) 
    { 
     if (base.AssociatedObject != null) 
     { 
      ICommand command = this.ResolveCommand(); 
      if ((command != null) && command.CanExecute(parameter)) 
      { 
       command.Execute(parameter); 
      } 
     } 
    } 

    private ICommand ResolveCommand() 
    { 
     ICommand command = null; 
     if (this.Command != null) 
     { 
      return this.Command; 
     } 
     if (base.AssociatedObject != null) 
     { 
      foreach (PropertyInfo info in base.AssociatedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
      { 
       if (typeof(ICommand).IsAssignableFrom(info.PropertyType) && string.Equals(info.Name, this.CommandName, StringComparison.Ordinal)) 
       { 
        command = (ICommand)info.GetValue(base.AssociatedObject, null); 
       } 
      } 
     } 
     return command; 
    } 

    private string commandName; 
    public string CommandName 
    { 
     get 
     { 
      base.ReadPreamble(); 
      return this.commandName; 
     } 
     set 
     { 
      if (this.CommandName != value) 
      { 
       base.WritePreamble(); 
       this.commandName = value; 
       base.WritePostscript(); 
      } 
     } 
    } 

    #region Command 
    public ICommand Command 
    { 
     get { return (ICommand)GetValue(CommandProperty); } 
     set { SetValue(CommandProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Command. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty CommandProperty = 
     DependencyProperty.Register("Command", typeof(ICommand), typeof(InteractiveCommand), new UIPropertyMetadata(null)); 
    #endregion 
} 

Come con qualsiasi codice si recupera da Internet, io lo consigliamo vivamente la lettura attraverso tutta la classe, e cercando di capire cosa fa. Non limitarti a lanciarlo nella tua app.

Ora possiamo fare:

<Button Content="I am a button"> 
    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="MouseEnter"> 
      <local:InteractiveCommand Command="{Binding CommandWithEventArgs}" /> 
     </i:EventTrigger> 
    </i:Interaction.Triggers> 
</Button> 

E il codice dietro:

#region CommandWithEventArgs 
DelegateCommand<MouseEventArgs> _CommandWithEventArgs; 
/// <summary> 
/// Exposes <see cref="CommandWithEventArgs(MouseEventArgs)"/>. 
/// </summary> 
public DelegateCommand<MouseEventArgs> CommandWithEventArgs 
{ 
    get { return _CommandWithEventArgs ?? (_CommandWithEventArgs = new DelegateCommand<MouseEventArgs>(CommandWithEventArgs)); } 
} 
#endregion 
public void CommandWithEventArgs(MouseEventArgs param) 
{ 
} 

E questo è un wrap;)

EDIT: per Silverlight, utilizzare questo codice invece:

public class InteractiveCommand : TriggerAction<DependencyObject> 
    { 
     protected override void Invoke(object parameter) 
     { 
      if (base.AssociatedObject != null) 
      { 
       ICommand command = Command; 
       if ((command != null) && command.CanExecute(parameter)) 
       { 
        command.Execute(parameter); 
       } 
      } 
     } 

     #region Command 
     public ICommand Command 
     { 
      get { return (ICommand)GetValue(CommandProperty); } 
      set { SetValue(CommandProperty, value); } 
     } 

     // Using a DependencyProperty as the backing store for Command. This enables animation, styling, binding, etc... 
     public static readonly DependencyProperty CommandProperty = 
      DependencyProperty.Register("Command", typeof(ICommand), typeof(InteractiveCommand), new UIPropertyMetadata(null)); 
     #endregion 
    } 

Ma si ricorda che è meno sicuro rispetto all'utilizzo della versione completa WPF (non controlla i tipi, può bloccarsi con elementi congelati).

+0

Penso che sia molto flessibile fuori dal box, che porta a scrivere varie estensioni per supportare cose che penseremmo fondamentali. –

+0

Ho appena provato il tuo codice, anche se ho avuto l'idea che il codice stesso non funzioni. (si copia/incolla da un posto migliore?) In pratica CommandName non viene mai assegnato e quindi ResolveCommand restituisce null e il tutto non funziona a causa di ciò. Ci sono anche alcuni problemi minori che assomigliano a base.WritePostscript(); non sono definiti in una classe base, ma non è importante –

+0

Ok, ti ​​preghiamo di modificare il tuo codice con lo snippet mobile che ha dimostrato di funzionare: http://pastie.org/5437478, http://pastie.org/5437483 Grazie di nuovo e buona giornata! –

35

Ho provato l'InteractiveCommand e ha causato problemi per me. Invece ho impostato un riferimento a Microsoft.Expression.Interactions e incluso

xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 

<i:Interaction.Triggers> 
    <i:EventTrigger EventName="AppointmentEditing"> 
     <ei:CallMethodAction MethodName="AppointmentEditing" TargetObject="{Binding}" /> 
    </i:EventTrigger> 
    <i:EventTrigger EventName="ShowDialog"> 
     <ei:CallMethodAction MethodName="ShowDialog" TargetObject="{Binding}" /> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

... nel mio UserControl.

poi mettere i gestori di eventi nel mio ViewModel e impostare l'ambito pubblico:

public void ShowDialog(object sender, ShowDialogEventArgs e) { 

} 

public void AppointmentEditing(object sender, AppointmentEditingEventArgs e) { 

} 

Lavorare bene finora.

+1

Questo è molto utile. – rajibdotnet

+5

Grande !! Questo evita la sottoclasse e consenti l'uso di comandi come eventhandlers dal codice code – isra60

+4

Per me, questa dovrebbe essere la risposta accettata –