将ListView的SelectedItems绑定到ViewModel上

21

我有一个列表视图,将项目与视图模型中的属性绑定。

<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");
        }
}

如何在视图模型中获取businesscollection的所选项?

2个回答

45

1. 获得数据绑定的一种方法:

您需要使用 SelectionChanged 事件。最简单的方法是在代码后台编写事件处理程序,将 "所选项绑定" 到视图模型。

//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设计,因为视图和视图模型的职责保持分开。您不需要在代码后台中编写任何逻辑,而视图模型则干净且可测试。

2.双向绑定:

如果您还需要在视图模型更改时更新视图,则必须将其附加到ViewModel的PropertyChanged事件和所选项的CollectionChanged事件。当然,您可以在代码后台中执行此操作,但在这种情况下,我会创建更可重复使用的内容:

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

非常感谢,这对我帮助很大 :) - ar.gorgin
1
感谢您提供的代码。您还应该在Collection_CollectionChanged方法中处理集合重置。 - Mehrzad Chehraz
1
你可能写了一些混淆的代码,类名是 ListViewXxx 但实现却是 ListBoxXxx。 - walterlv
我在CollectionChanged方法中添加了Reset的处理。 - runemoennike

-4

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

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

将其作为您的 VM 属性进行包装。 希望能有所帮助!

谢谢,但 BusinessCollection 没有 IsSelected 属性。 - ar.gorgin
对不起,我没有检查那个。为什么不使用另一个集合呢?使用ObservableCollection,我知道它可以工作,因为我以前就是这样实现的。 - WpfFanatic
这与绑定无关。 - runemoennike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接