2015-07-02 11 views
6

Ho una vista di elenco che associa elementi con una proprietà in viewmodel.Binding SelectedItems di ListView su ViewModel

<ListView Height="238" 
      HorizontalAlignment="Left" 
      Name="listView" 
      VerticalAlignment="Top" 
      Width="503" 
      ItemsSource="{Binding BusinessCollection}" 
      SelectionMode="Multiple" 
      > 
    <ListView.View> 
     <GridView> 
      <GridView.Columns> 
       <GridViewColumn> 
        <GridViewColumn.CellTemplate> 
         <DataTemplate> 
          <CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}, Path=IsSelected}" /> 
         </DataTemplate> 
        </GridViewColumn.CellTemplate> 
       </GridViewColumn> 
       <GridViewColumn DisplayMemberBinding="{Binding ID}" Header="ID" /> 
       <GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Name" /> 
      </GridView.Columns> 
     </GridView> 
    </ListView.View> 
</ListView> 

e in vista.

ICollectionView _businessCollection 
public ICollectionView BusinessCollection 
{ 
get {return _businessCollection;} 
set{ 
_businessCollection=value; 
RaisePropertyOnChange("BusinessCollection"); 
} 
} 

Come ottenere l'articolo selezionato di businesscollection in viewmodel?

risposta

10

1. Un modo per vincolante fonte:

è necessario utilizzare SelectionChanged evento. Il modo più semplice è scrivere eventhandler in codebehind per "associare selecteditems" a viewmodel.

//ViewModel 
public ICollectionView BusinessCollection {get; set;} 
public List<YourBusinessItem> SelectedObject {get; set;} 

//Codebehind 
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    var viewmodel = (ViewModel) DataContext; 
    viewmodel.SelectedItems = listview.SelectedItems 
     .Cast<YourBusinessItem>() 
     .ToList(); 
} 

Ciò allinea ancora con disegno MVVM, perché vista e ViewModel resposibilities sono mantenuti separati. Non hai alcuna logica in codebehind e viewmodel è pulito e testabile.

2. Due modo vincolante:

se anche è necessario aggiornare vista, quando i cambiamenti ViewModel, è necessario allegare alla manifestazione di ViewModel PropertyChanged e CollectionChanged caso gli elementi selezionati. naturalmente è possibile farlo in codebehind, ma in questo caso vorrei creare qualcosa di più riutilizzabile:

//ViewModel 
public ObservableCollection<YourBusinessItem> SelectedObject {get; set;} 

//in codebehind: 
var binder = new SelectedItemsBinder(listview, viewmodel.SelectedItems); 
binder.Bind(); 

o creare proprietà associata personalizzato, in modo da poter utilizzare la sintassi obbligatorio in XAML:

<ListView local:ListViewExtensions.SelectedValues="{Binding SelectedItem}" .../> 
public class SelectedItemsBinder 
{ 
    private ListView _listBox; 
    private IList _collection; 


    public SelectedItemsBinder(ListView listBox, IList collection) 
    { 
     _listBox = listBox; 
     _collection = collection; 

     _listBox.SelectedItems.Clear(); 

     foreach (var item in _collection) 
     { 
      _listBox.SelectedItems.Add(item); 
     } 
    } 

    public void Bind() 
    { 
     _listBox.SelectionChanged += ListBox_SelectionChanged; 

     if (_collection is INotifyCollectionChanged) 
     { 
      var observable = (INotifyCollectionChanged) _collection; 
      observable.CollectionChanged += Collection_CollectionChanged; 
     } 
    } 

    public void UnBind() 
    { 
     if (_listBox != null) 
      _listBox.SelectionChanged -= ListBox_SelectionChanged; 

     if (_collection != null && _collection is INotifyCollectionChanged) 
     { 
      var observable = (INotifyCollectionChanged) _collection; 
      observable.CollectionChanged -= Collection_CollectionChanged; 
     } 
    } 

    private void Collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     foreach (var item in e.NewItems ?? new object[0]) 
     { 
      if (!_listBox.SelectedItems.Contains(item)) 
       _listBox.SelectedItems.Add(item); 
     } 
     foreach (var item in e.OldItems ?? new object[0]) 
     { 
      _listBox.SelectedItems.Remove(item); 
     } 
    } 

    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     foreach (var item in e.AddedItems ?? new object[0]) 
     { 
      if (!_collection.Contains(item)) 
       _collection.Add(item); 
     } 

     foreach (var item in e.RemovedItems ?? new object[0]) 
     { 
      _collection.Remove(item); 
     } 
    } 
} 

proprietà associata implementazione

public class ListBoxExtensions 
{ 

    private static SelectedItemsBinder GetSelectedValueBinder(DependencyObject obj) 
    { 
     return (SelectedItemsBinder)obj.GetValue(SelectedValueBinderProperty); 
    } 

    private static void SetSelectedValueBinder(DependencyObject obj, SelectedItemsBinder items) 
    { 
     obj.SetValue(SelectedValueBinderProperty, items); 
    } 

    private static readonly DependencyProperty SelectedValueBinderProperty = DependencyProperty.RegisterAttached("SelectedValueBinder", typeof(SelectedItemsBinder), typeof(ListBoxExtensions)); 


    public static readonly DependencyProperty SelectedValuesProperty = DependencyProperty.RegisterAttached("SelectedValues", typeof(IList), typeof(ListBoxExtensions), 
     new FrameworkPropertyMetadata(null, OnSelectedValuesChanged)); 


    private static void OnSelectedValuesChanged(DependencyObject o, DependencyPropertyChangedEventArgs value) 
    { 
     var oldBinder = GetSelectedValueBinder(o); 
     if (oldBinder != null) 
      oldBinder.UnBind(); 

     SetSelectedValueBinder(o, new SelectedItemsBinder((ListBox)o, (IList)value.NewValue)); 
     GetSelectedValueBinder(o).Bind(); 
    } 

    public static void SetSelectedValues(Selector elementName, IEnumerable value) 
    { 
     elementName.SetValue(SelectedValuesProperty, value); 
    } 

    public static IEnumerable GetSelectedValues(Selector elementName) 
    { 
     return (IEnumerable)elementName.GetValue(SelectedValuesProperty); 
    } 
} 
+0

Grazie mille, mi ha aiutato molto :) –

+0

Grazie per il codice. Dovresti anche gestire il reset della raccolta nel metodo Collection_CollectionChanged. –

-2

Dal momento che l'ItemSource è BusinessCollection, si dovrebbe essere in grado di:

var selectedItems = BusinessCollection.Where(x => x.IsSelected); 

avvolgerlo come una proprietà sulla vostra macchina virtuale. Spero che aiuti!

+0

Grazie, ma Busine ssCollection non ha Proprietà IsSelected. –

+0

Scusa, non l'ho verificato. Perché allora non hai usato una collezione diversa? Con ObservableCollection, so che funziona perché l'ho implementato in quel modo prima. – WpfFanatic

+0

Come si usa? –

Problemi correlati