如何在WPF MVVM模式下正确获取DataGrid选中行数?
问题原因
你用SelectedItem绑定SelItm,但SelectedItem仅对应单个选中项,在Extended多选模式下根本无法获取所有选中行,所以调用SelItm.Count()自然会出错。
符合MVVM的正确实现方式
WPF的DataGrid.SelectedItems是只读集合,不能直接绑定到ViewModel,需要通过自定义附加属性实现选中项集合与ViewModel属性的双向同步,下面是具体实现步骤:
步骤1:创建自定义附加属性类
新建静态类,封装选中项同步逻辑:
using System.Collections; using System.Windows; using System.Windows.Controls; public static class DataGridSelectedItemsBehavior { public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached( "SelectedItems", typeof(IList), typeof(DataGridSelectedItemsBehavior), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsChanged)); public static IList GetSelectedItems(DependencyObject obj) { return (IList)obj.GetValue(SelectedItemsProperty); } public static void SetSelectedItems(DependencyObject obj, IList value) { obj.SetValue(SelectedItemsProperty, value); } private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is DataGrid dataGrid) { dataGrid.SelectionChanged -= DataGrid_SelectionChanged; if (e.NewValue is IList newSelectedItems) { dataGrid.SelectionChanged += DataGrid_SelectionChanged; // 同步初始选中项 dataGrid.SelectedItems.Clear(); foreach (var item in newSelectedItems) { dataGrid.SelectedItems.Add(item); } } } } private static void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender is DataGrid dataGrid) { var selectedItems = GetSelectedItems(dataGrid); if (selectedItems == null) return; // 移除取消选中的项 foreach (var item in e.RemovedItems) { if (selectedItems.Contains(item)) { selectedItems.Remove(item); } } // 添加新选中的项 foreach (var item in e.AddedItems) { if (!selectedItems.Contains(item)) { selectedItems.Add(item); } } } } }
步骤2:修改ViewModel代码
添加选中项集合属性和选中行数字段,确保ViewModel实现INotifyPropertyChanged接口:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; public class YourViewModel : INotifyPropertyChanged { private ICollectionView _allFilteredStudents; public ICollectionView FilteredStudents { get { if(_allFilteredStudents == null) { this._allFilteredStudents = new ListCollectionView(GetAllStudents()) { Filter = o => ((Students)o).Name != "Ram" }; } return _allFilteredStudents; } } private ObservableCollection<Students> _selectedStudents; public ObservableCollection<Students> SelectedStudents { get => _selectedStudents; set { _selectedStudents = value; OnPropertyChanged(); SelectedRowCount = _selectedStudents.Count; } } private int _selectedRowCount; public int SelectedRowCount { get => _selectedRowCount; set { _selectedRowCount = value; OnPropertyChanged(); } } public YourViewModel() { SelectedStudents = new ObservableCollection<Students>(); } // 模拟获取学生数据方法 private List<Students> GetAllStudents() { return new List<Students>(); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } // 学生实体类示例 public class Students { public string Name { get; set; } public string Address { get; set; } public int Age { get; set; } }
步骤3:修改DataGrid的XAML代码
移除原有SelectedItem绑定,添加自定义附加属性绑定:
<Window x:Class="YourNamespace.YourWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:YourNamespace"> <Grid> <DataGrid ItemsSource="{Binding FilteredStudents}" SelectionMode="Extended" AutoGenerateColumns="False" IsReadOnly="True" SelectionUnit="FullRow" HeadersVisibility="Column" local:DataGridSelectedItemsBehavior.SelectedItems="{Binding SelectedStudents, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> <DataGrid.Columns> <DataGridTextColumn Header="Bill No." Binding="{Binding Path=Name, Mode=OneWay}" Width="260" IsReadOnly="True" /> <DataGridTextColumn Header="Bill Date" Binding="{Binding Path=Address, Mode=OneWay}" Width="200" IsReadOnly="True" /> <DataGridTextColumn Header="Amount" Binding="{Binding Path=Age, Mode=OneWay}" Width="210" IsReadOnly="True" /> </DataGrid.Columns> </DataGrid> <!-- 绑定选中行数的标签 --> <Label Content="{Binding SelectedRowCount}" Margin="10,0"/> </Grid> </Window>
原理说明
自定义附加属性通过监听DataGrid.SelectionChanged事件,将界面选中项同步到ViewModel的SelectedStudents集合,同时支持从ViewModel反向设置选中项。此后你可以通过SelectedStudents.Count准确获取选中行数,完全符合MVVM模式的解耦要求。
内容的提问来源于stack exchange,提问作者Tamal Banerjee
相关产品推荐
相关产品推荐

