2015-07-02 12 views
6

Mam widok listy wiążące elementy z właściwość w viewmodel.Powiązanie wybranych elementów ListView do 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> 

oraz w widoku modelu.

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

Jak zdobyć wybrany przedmiot w viewmodelu?

Odpowiedz

10

1. Jednym ze sposobów wiązania źródło:

Musisz użyć SelectionChanged wydarzenie. Najprostszym sposobem jest napisanie programu obsługi zdarzeń w codebehind, aby "powiązać selecteditems" z 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(); 
} 

To nadal wyrównuje się z projektem MVVM, ponieważ zakresy widoku i viewmodel są oddzielone. Nie masz żadnej logiki w codebehind i viewmodel jest czysty i testowalny.

2. Dwa sposób wiążący:

jeśli trzeba także zaktualizować widok, kiedy ViewModel zmiany, trzeba dołączyć do PropertyChanged przypadku ViewModel i do CollectionChanged przypadku wybranych pozycji. Oczywiście można to zrobić w kodzie, ale w tym przypadku chciałbym stworzyć coś bardziej wielokrotnego użytku:

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

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

lub mogą tworzyć niestandardowe załączony właściwość, dzięki czemu można używać składni wiążącej w 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); 
     } 
    } 
} 

Dołączone nieruchomość realizacja

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

Wielkie dzięki, bardzo mi to pomogło :) –

+0

Dzięki za kod. Powinieneś również obsługiwać resetowanie kolekcji w metodzie Collection_CollectionChanged. –

-2

Od ItemSource jest BusinessCollection, powinieneś być w stanie:

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

Owiń ją jako własność na swojej VM. Mam nadzieję, że to pomoże!

+0

Dzięki, ale Busine ssCollection nie ma właściwości IsSelected. –

+0

Niestety nie sprawdziłem tego. Dlaczego więc nie użyć innej kolekcji? Dzięki ObservableCollection wiem, że to działa, ponieważ wcześniej to zaimplementowałem. – WpfFanatic

+0

Jak z niego korzystać? –

Powiązane problemy