将ListView的SelectedItem绑定到ViewModel

时间:2021-04-05 16:05:21

I have a list view that binding items with a property in viewmodel.

我有一个列表视图,在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>

and in viewmodel.

并在viewmodel中。

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

How to get selected item of businesscollection in viewmodel?

如何在viewmodel中获取businesscollection的选定项?

2 个解决方案

#1


13  

1. One way to source binding:

1.源绑定的一种方法:

You have to use SelectionChanged event. The easiest way is to write eventhandler in codebehind to "bind selecteditems" to viewmodel.

您必须使用SelectionChanged事件。最简单的方法是在codebehind中编写eventhandler以将“binditems”绑定到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();
}

This still aligns with MVVM design, because view and viewmodel resposibilities are kept separated. You dont have any logic in codebehind and viewmodel is clean and testable.

这仍然与MVVM设计保持一致,因为视图和视图模型的可靠性保持分离。您在代码隐藏中没有任何逻辑,并且viewmodel是干净且可测试的。

2. Two way binding:

2.双向绑定:

if you also need to update view, when viewmodel changes, you have to attach to ViewModel's PropertyChanged event and to the selected items' CollectionChanged event. of course you can do it in codebehind, but in this case I would create something more reusable:

如果您还需要更新视图,则当viewmodel更改时,您必须附加到ViewModel的PropertyChanged事件和所选项的CollectionChanged事件。当然你可以在代码隐藏中做到这一点,但在这种情况下我会创建一些更可重用的东西:

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

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

or can create custom attached property, so you can use binding syntax in xaml:

或者可以创建自定义附加属性,因此您可以在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);
        }
    }
}

Attached property implementation

附属物实施

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

#2


-2  

Since the itemSource is BusinessCollection, you should be able to:

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

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

Wrap it as a property on your VM. Hope it helps!

将其作为VM上的属性包装。希望能帮助到你!

#1


13  

1. One way to source binding:

1.源绑定的一种方法:

You have to use SelectionChanged event. The easiest way is to write eventhandler in codebehind to "bind selecteditems" to viewmodel.

您必须使用SelectionChanged事件。最简单的方法是在codebehind中编写eventhandler以将“binditems”绑定到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();
}

This still aligns with MVVM design, because view and viewmodel resposibilities are kept separated. You dont have any logic in codebehind and viewmodel is clean and testable.

这仍然与MVVM设计保持一致,因为视图和视图模型的可靠性保持分离。您在代码隐藏中没有任何逻辑,并且viewmodel是干净且可测试的。

2. Two way binding:

2.双向绑定:

if you also need to update view, when viewmodel changes, you have to attach to ViewModel's PropertyChanged event and to the selected items' CollectionChanged event. of course you can do it in codebehind, but in this case I would create something more reusable:

如果您还需要更新视图,则当viewmodel更改时,您必须附加到ViewModel的PropertyChanged事件和所选项的CollectionChanged事件。当然你可以在代码隐藏中做到这一点,但在这种情况下我会创建一些更可重用的东西:

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

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

or can create custom attached property, so you can use binding syntax in xaml:

或者可以创建自定义附加属性,因此您可以在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);
        }
    }
}

Attached property implementation

附属物实施

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

#2


-2  

Since the itemSource is BusinessCollection, you should be able to:

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

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

Wrap it as a property on your VM. Hope it helps!

将其作为VM上的属性包装。希望能帮助到你!