wpf 将 ListView 的 SelectedItems 绑定到 ViewModel
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31176949/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Binding SelectedItems of ListView to ViewModel
提问by ar.gorgin
I have a list view that binding items with a property in 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.
并在视图模型中。
ICollectionView _businessCollection
public ICollectionView BusinessCollection
{
get { return _businessCollection; }
set {
_businessCollection = value;
RaisePropertyOnChange("BusinessCollection");
}
}
How to get selected item of businesscollection in viewmodel?
如何在视图模型中获取业务集合的选定项目?
回答by Liero
1. One way to source binding:
1.源绑定的一种方式:
You have to use SelectionChangedevent. The easiest way is to write eventhandler in codebehind to "bind selecteditems" to viewmodel.
你必须使用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();
}
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 设计一致,因为视图和视图模型的职责是分开的。您在代码隐藏中没有任何逻辑,并且视图模型干净且可测试。
2. Two way binding:
2. 双向绑定:
if you also need to update view, when viewmodel changes, you have to attach to ViewModel's PropertyChangedevent and to the selected items' CollectionChangedevent. of course you can do it in codebehind, but in this case I would create something more reusable:
如果您还需要更新视图,当视图模型更改时,您必须附加到 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);
}
}
回答by WpfFanatic
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 上的属性。希望能帮助到你!

