wpf mvvm listbox

WPF MV-VM: Obtener elementos seleccionados de un ListCollectionView?



mvvm listbox (9)

Necesita crear un ViewModel que tenga el concepto de IsSelected y esté vinculado a la propiedad IsSelected del ListBoxItem actual que lo represente en la Vista utilizando la arquitectura de enlaces WPF estándar.

Luego, en su código, que conoce su ViewModel, pero no el hecho de que esté representado por una Vista específica, puede usar esa propiedad para averiguar qué elementos del Modelo se seleccionan independientemente de la elección del diseñador de cómo se representa en el Ver.

Tengo una aplicación WPF que usa el patrón Model-View-ViewModel.
En mi ViewModel tengo un ListCollectionView para mantener una lista de elementos.
Este ListCollectionView está vinculado a un ListBox en mi vista.

<ListBox Grid.Row="1" ItemsSource="{Binding Useragents}" SelectionMode="Multiple"/>

ListBox tiene SelectionMode = Multiple, por lo que puede seleccionar más elementos a la vez. Ahora el ViewModel necesita saber qué elementos se han seleccionado.

El problema es: en el modelo View-Model-ViewModel, ViewModel no tiene acceso a la Vista, por lo que no puedo simplemente preguntarle a ListBox qué elementos se han seleccionado. Todo lo que tengo es ListCollectionView, pero no puedo encontrar la manera de encontrar qué elementos se han seleccionado allí.

Entonces, ¿cómo puedo encontrar qué elementos se han seleccionado en el ListBox? O un truco para lograr esto (¿quizás vincular algo a un booleano ''IsSelected'' en mis elementos? ¿Pero qué? ¿Cómo?)

Tal vez alguien que está usando este patrón, también, ¿puede ayudarme aquí?


La solución de Drew Marsh funciona muy bien, lo recomiendo. ¡Y tengo otra solución!

Model View ViewModel es una vista pasiva , también puede usar un modelo de presentación para acceder a algunos datos de su presentación sin estar acoplado con WPF (este patrón se usa en el ejemplo de Stocktrader de PRISM ).



Aquí hay otra variante del patrón View-Model-ViewModel donde ViewModel tiene acceso a la vista a través de una interfaz IView.

Encontré bastantes escenarios en los que no puedes usar el enlace WPF y luego necesitas una forma de código para sincronizar el estado entre la Vista y el Modelo de Vista.

Cómo se puede hacer esto se muestra aquí:

Marco de aplicación de WPF (WAF)


La respuesta de Drew Marsh está bien si tienes una lista pequeña, si tienes una lista grande, el rendimiento alcanzado para encontrar todos los elementos seleccionados podría ser desagradable. Mi solución favorita es crear una propiedad adjunta en su ListBox que luego se une a un ObservableCollection que contiene los elementos seleccionados. Luego, con su propiedad adjunta, se suscribe al evento SelectionChanged de los elementos para agregar / eliminar elementos de su colección.



La implementación de referencia de PRISM MVVM tiene un comportamiento llamado SynchronizeSelectedItems, utilizado en Prism4 / MVVM RI / MVVM.Client / Views / MultipleSelectionView.xaml, que sincroniza los elementos marcados con la propiedad ViewModel denominada Selections :

<ListBox Grid.Column="0" Grid.Row="1" IsTabStop="False" SelectionMode="Multiple" ItemsSource="{Binding Question.Range}" Margin="5"> <ListBox.ItemContainerStyle> <!-- Custom style to show the multi-selection list box as a collection of check boxes --> <Style TargetType="ListBoxItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <Grid Background="Transparent"> <CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" IsHitTestVisible="False" IsTabStop="True" AutomationProperties.AutomationId="CheckBoxAutomationId"> <ContentPresenter/> </CheckBox> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </ListBox.ItemContainerStyle> <i:Interaction.Behaviors> <!-- Custom behavior that synchronizes the selected items with the view models collection --> <Behaviors:SynchronizeSelectedItems Selections="{Binding Selections}"/> </i:Interaction.Behaviors> </ListBox>

Vaya a http://compositewpf.codeplex.com/ y agárrelo todo o use esto:

//=================================================================================== // Microsoft patterns & practices // Composite Application Guidance for Windows Presentation Foundation and Silverlight //=================================================================================== // Copyright (c) Microsoft Corporation. All rights reserved. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE. //=================================================================================== // The example companies, organizations, products, domain names, // e-mail addresses, logos, people, places, and events depicted // herein are fictitious. No association with any real company, // organization, product, domain name, email address, logo, person, // places, or events is intended or should be inferred. //=================================================================================== using System; using System.Collections; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; namespace MVVM.Client.Infrastructure.Behaviors { /// <summary> /// Custom behavior that synchronizes the list in <see cref="ListBox.SelectedItems"/> with a collection. /// </summary> /// <remarks> /// This behavior uses a weak event handler to listen for changes on the synchronized collection. /// </remarks> public class SynchronizeSelectedItems : Behavior<ListBox> { public static readonly DependencyProperty SelectionsProperty = DependencyProperty.Register( "Selections", typeof(IList), typeof(SynchronizeSelectedItems), new PropertyMetadata(null, OnSelectionsPropertyChanged)); private bool updating; private WeakEventHandler<SynchronizeSelectedItems, object, NotifyCollectionChangedEventArgs> currentWeakHandler; [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Dependency property")] public IList Selections { get { return (IList)this.GetValue(SelectionsProperty); } set { this.SetValue(SelectionsProperty, value); } } protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.SelectionChanged += this.OnSelectedItemsChanged; this.UpdateSelectedItems(); } protected override void OnDetaching() { this.AssociatedObject.SelectionChanged += this.OnSelectedItemsChanged; base.OnDetaching(); } private static void OnSelectionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = d as SynchronizeSelectedItems; if (behavior != null) { if (behavior.currentWeakHandler != null) { behavior.currentWeakHandler.Detach(); behavior.currentWeakHandler = null; } if (e.NewValue != null) { var notifyCollectionChanged = e.NewValue as INotifyCollectionChanged; if (notifyCollectionChanged != null) { behavior.currentWeakHandler = new WeakEventHandler<SynchronizeSelectedItems, object, NotifyCollectionChangedEventArgs>( behavior, (instance, sender, args) => instance.OnSelectionsCollectionChanged(sender, args), (listener) => notifyCollectionChanged.CollectionChanged -= listener.OnEvent); notifyCollectionChanged.CollectionChanged += behavior.currentWeakHandler.OnEvent; } behavior.UpdateSelectedItems(); } } } private void OnSelectedItemsChanged(object sender, SelectionChangedEventArgs e) { this.UpdateSelections(e); } private void UpdateSelections(SelectionChangedEventArgs e) { this.ExecuteIfNotUpdating( () => { if (this.Selections != null) { foreach (var item in e.AddedItems) { this.Selections.Add(item); } foreach (var item in e.RemovedItems) { this.Selections.Remove(item); } } }); } private void OnSelectionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { this.UpdateSelectedItems(); } private void UpdateSelectedItems() { this.ExecuteIfNotUpdating( () => { if (this.AssociatedObject != null) { this.AssociatedObject.SelectedItems.Clear(); foreach (var item in this.Selections ?? new object[0]) { this.AssociatedObject.SelectedItems.Add(item); } } }); } private void ExecuteIfNotUpdating(Action execute) { if (!this.updating) { try { this.updating = true; execute(); } finally { this.updating = false; } } } } }


Para mí, la mejor respuesta es romper un poco el principio de MVVM.

En el código subyacente 1. Instanciate tu viewModel 2. agrega un manejador de eventos SelectionChanged 3. itera a través de los elementos seleccionados y agrega cada elemento a tu lista de viewModel

ViewModel viewModel = new ViewModel(); viewModel.SelectedModules = new ObservableCollection<string>(); foreach (var selectedModule in listBox1.SelectedItems) { viewModel.SelectedModules.Add(selectedModule.ToString()); }