【问题标题】:Binding SelectedItems of ListView to ViewModel将 ListView 的 SelectedItems 绑定到 ViewModel
【发布时间】:2021-09-23 20:06:16
【问题描述】:

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

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

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

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

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

    【讨论】:

    • 非常感谢,对我帮助很大:)
    • 感谢您的代码。您还应该在 Collection_CollectionChanged 方法中处理集合重置。
    • 您可能编写了混淆代码,认为您的类名是 ListViewXxx 但实现是 ListBoxXxx。
    • 我在 CollectionChanged 方法中添加了对 Reset 的处理。
    【解决方案2】:

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

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

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

    【讨论】:

    • 谢谢,但 BusinessCollection 没有 IsSelected 属性。
    • 抱歉没有检查。为什么不使用不同的集合呢?使用 ObservableCollection,我知道它可以工作,因为我之前已经实现过它。
    • 这与绑定无关。
    猜你喜欢
    • 2014-12-08
    • 2015-06-08
    • 1970-01-01
    • 1970-01-01
    • 2015-07-24
    • 1970-01-01
    • 1970-01-01
    • 2018-05-10
    • 1970-01-01
    相关资源
    最近更新 更多