将ListView的SelectedItems绑定到ViewModel

本文关键字:ViewModel 绑定 SelectedItems ListView | 更新日期: 2023-09-27 18:00:50

我有一个列表视图,它将项目与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>

以及在视图模型中。

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

如何在视图模型中获取业务集合的选定项目?

将ListView的SelectedItems绑定到ViewModel

1。源绑定的一种方法:

您必须使用SelectionChanged事件。最简单的方法是在codeehind中编写事件处理程序,将selecteditems"绑定到视图模型"。

//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();
}

这仍然与MVVM设计一致,因为视图和视图模型的责任是分开的。您在codeehind中没有任何逻辑,视图模型是干净的、可测试的。

2.双向绑定:

如果还需要更新视图,则在视图模型更改时,必须附加到viewmodel的PropertyChanged事件和所选项目的CollectionChanged事件。当然,你可以在codebehind中完成,但在这种情况下,我会创建一些更可重用的东西:

//ViewModel
public ObservableCollection<YourBusinessItem> SelectedObject {get; set;}
//in codebehind:
var binder = new SelectedItemsBinder(listview, viewmodel.SelectedItems);
binder.Bind();

或者可以创建自定义附加属性,因此可以在xaml:中使用绑定语法

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

    public SelectedItemsBinder(ListView listView, IList collection)
    {
        _listView = listView;
        _collection = collection;
        _listView.SelectedItems.Clear();
        foreach (var item in _collection)
        {
            _listView.SelectedItems.Add(item);
        }
    }
    public void Bind()
    {
        _listView.SelectionChanged += ListView_SelectionChanged;
        if (_collection is INotifyCollectionChanged)
        {
            var observable = (INotifyCollectionChanged) _collection;
            observable.CollectionChanged += Collection_CollectionChanged;
        }
    }
    public void UnBind()
    {
        if (_listView != null)
            _listView.SelectionChanged -= ListView_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 (!_listView.SelectedItems.Contains(item))
                _listView.SelectedItems.Add(item);
        }
        foreach (var item in e.OldItems ?? new object[0])
        {
            _listView.SelectedItems.Remove(item);
        }
    }
    private void ListView_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);
        }
    }
}

附加属性实现

public class ListViewExtensions
{
    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(ListViewExtensions));

    public static readonly DependencyProperty SelectedValuesProperty = DependencyProperty.RegisterAttached("SelectedValues", typeof(IList), typeof(ListViewExtensions),
        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((ListView)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);
    }
}

由于itemSource是BusinessCollection,您应该能够:

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

将其包装为VM上的属性。希望它能有所帮助!