2011-02-04 27 views
40

Estoy haciendo una refacturación de una aplicación simple para seguir MVVM y mi pregunta es ¿cómo puedo mover un evento SelectionChanged fuera de mi código al viewModel? He visto algunos ejemplos de elementos vinculantes para los comandos pero no los entendí del todo. ¿Alguien puede ayudar con esto? ¡Gracias!WPF Vinculación de eventos de UI a comandos en ViewModel

¿Alguien puede proporcionar una solución utilizando el siguiente código? ¡Muchas gracias!

public partial class MyAppView : Window 
{ 
    public MyAppView() 
    { 
     InitializeComponent(); 

     this.DataContext = new MyAppViewModel(); 

     // Insert code required on object creation below this point. 
    } 

    private void contactsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) 
    { 
     //TODO: Add event handler implementation here.   
     //for each selected contact get the labels and put in collection 

     ObservableCollection<AggregatedLabelModel> contactListLabels = new ObservableCollection<AggregatedLabelModel>(); 

     foreach (ContactListModel contactList in contactsList.SelectedItems) 
     { 
      foreach (AggregatedLabelModel aggLabel in contactList.AggLabels) 
      { 
       contactListLabels.Add(aggLabel); 
      } 
     } 
     //aggregate the contactListLabels by name 
     ListCollectionView selectedLabelsView = new ListCollectionView(contactListLabels); 

     selectedLabelsView.GroupDescriptions.Add(new PropertyGroupDescription("Name")); 
     tagsList.ItemsSource = selectedLabelsView.Groups; 
    } 
} 

Respuesta

83

Debe utilizar un EventTrigger en combinación con InvokeCommandAction del espacio de nombres Windows.Interactivity. He aquí un ejemplo:

<ListBox ...> 
    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="SelectionChanged"> 
      <i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/> 
     </i:EventTrigger> 
    </i:Interaction.Triggers> 
</ListBox> 

Puede hacer referencia a System.Windows.Interactivity yendo Add reference > Assemblies > Extensions.

Y el espacio de nombres completo i es: xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity".

+0

Gracias. Soy un novato en la programación, así que discúlpeme: ¿puede dar un ejemplo con el código que le proporcioné? – Ben

+1

Básicamente necesita crear una propiedad de comando en su ViewModel llamada "SelectedItemChangedCommand". Commanding es similar a los eventos, pero un comando solo puede tener una función de devolución de llamada, a diferencia de los eventos. Verifique los documentos: http://msdn.microsoft.com/en-us/library/ms752308.aspx – Brian

+1

Si no tiene Expression Blend, necesitará el SDK: http://www.microsoft.com/downloads/ es/details.aspx? FamilyID = D197F51A-DE07-4EDF-9CBA-1F1B4A22110D & displaylang = en – Murven

8

Esta pregunta tiene un problema similar.

WPF MVVM : Commands are easy. How to Connect View and ViewModel with RoutedEvent

A mi modo de abordar esta cuestión es tener una propiedad SelectedItem en el modelo de vista, y luego obligar a la SelectedItem del cuadro de lista o lo que sea a esa propiedad.

+1

punto válido, pero no aplicable a todos los escenarios ... Tengo un escenario en el que estoy haciendo lo que sugiere, pero también necesito usar el evento SelectionChanged – Jordan

0

Como menciona @Cameron MacFarland, me limitaría en dos sentidos a una propiedad en el modelo de vista. En el establecimiento de propiedades puede hacer cualquier lógica que necesite, como agregar a una lista de contactos, según sus requisitos.

Sin embargo, no necesariamente llamaría a la propiedad 'SelectedItem' ya que viewModel no debería saber sobre la capa de vista y cómo está interactuando con sus propiedades. Yo lo llamaría algo así como CurrentContact o algo así.

Obviamente, esto es menos que lo que desea es crear comandos como un ejercicio para practicar etc.

+1

No estoy de acuerdo que el modelo de vista no debería saber sobre la capa de vista. Es, después de todo, un modelo de la vista. No debe manipular objetos en la vista, pero solo para que pueda ser instanciado independientemente de la vista en pruebas unitarias. No llamaría a la propiedad 'SelectedItem' a menos que la colección del modelo de vista se llamara' Items', pero ese es un problema diferente. –

+0

Vea lo que quiere decir, pero tiendo a no percibir el vm como un "modelo de la vista", más como un adaptador que no hace suposiciones sobre la UI sino que simplemente expone su estado y comportamiento a través de comandos y notificaciones. Y esta separación no es "solo" para las pruebas unitarias, como dices, sino que también significa que la interfaz de usuario se puede cambiar fácilmente de entrada y salida y modificar cuando sea necesario sin la necesidad de modificar la máquina virtual. – HAdes

7

refactorizar ello, tiene que cambiar su forma de pensar. Ya no se manejará un evento de "selección modificada", sino que se almacenará el elemento seleccionado en su modelo de vista. Luego, utilizará el enlace de datos bidireccional para que cuando el usuario seleccione un elemento, su modelo de vista se actualice y cuando lo modifique, su vista se actualice.

+0

¡Creo que la parte del pensamiento de la mutación es donde estoy luchando por ser un programador bastante novato! – Ben

1

que siguieran la respuesta más común en este question

Básicamente el modelo de vista contendrá una lista de todos sus artículos y una lista de elementos seleccionados. A continuación, puede adjuntar un comportamiento a su listbox que gestiona su lista de elementos seleccionados.

Hacer esto significa que no tiene nada en el código y el xaml es bastante fácil de seguir, también el comportamiento se puede reutilizar en otra parte de su aplicación.

<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" /> 
2
<ListBox SelectionChanged="{eb:EventBinding Command=SelectedItemChangedCommand, CommandParameter=$e}"> 

</ListBox> 

Comando

{eb: EventBinding} (patrón de nomenclatura simple para encontrar Comando)

{eb: EventBinding Comando = CommandName}

CommandParameter

$ e (EventAgrs)

$ o $ this.Property este

cadena

https://github.com/JonghoL/EventBindingMarkup

1

A veces la solución del suceso de unión para ordenar a través de la interactividad de disparo no funciona, cuando es necesario para vincular el evento de usercontrol personalizado. En este caso, puede usar un comportamiento personalizado. comportamiento de enlace

Declarar como:

public class PageChangedBehavior 
{ 
    #region Attached property 

    public static ICommand PageChangedCommand(DependencyObject obj) 
    { 
     return (ICommand)obj.GetValue(PageChangedCommandProperty); 
    } 
    public static void SetPageChangedCommand(DependencyObject obj, ICommand value) 
    { 
     obj.SetValue(PageChangedCommandProperty, value); 
    } 

    public static readonly DependencyProperty PageChangedCommandProperty = 
     DependencyProperty.RegisterAttached("PageChangedCommand", typeof(ICommand), typeof(PageChangedBehavior), 
      new PropertyMetadata(null, OnPageChanged)); 

    #endregion 

    #region Attached property handler 

    private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var control = d as PageControl; 
     if (control != null) 
     { 
      if (e.NewValue != null) 
      { 
       control.PageChanged += PageControl_PageChanged; 
      } 
      else 
      { 
       control.PageChanged -= PageControl_PageChanged; 
      } 
     } 
    } 

    static void PageControl_PageChanged(object sender, int page) 
    { 
     ICommand command = PageChangedCommand(sender as DependencyObject); 

     if (command != null) 
     { 
      command.Execute(page); 
     } 
    } 

    #endregion 

}

Y luego se unen a mandar en xaml:

 <controls:PageControl 
      Grid.Row="2" 
      CurrentPage="{Binding Path=UsersSearchModel.Page,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
      PerPage="{Binding Path=UsersSearchModel.PageSize,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
      Count="{Binding Path=UsersSearchModel.SearchResults.TotalItemCount}" 
      behaviors:PageChangedBehavior.PageChangedCommand="{Binding PageChangedCommand}"> 
     </controls:PageControl> 
Cuestiones relacionadas