MVVM模式下如何通过代码选中自定义ListView多行?
解决MVVM模式下自定义ListView多行选中同步问题
看起来你遇到的核心问题是自定义ListView的SelectedItemsList双向绑定只实现了UI到ViewModel的单向同步,ViewModel到UI的反向同步没有正确工作。结合你提供的代码,我来一步步帮你修复:
1. 修复自定义SrListView的双向绑定逻辑
你之前尝试添加OnSelectedItemsListChanged回调,但只调用了SetSelectedItems,这里有几个关键问题需要完善:
- 要避免循环触发:当我们从ViewModel同步到UI时,会触发
SelectionChanged事件,这时候不能再把SelectedItems赋值回SelectedItemsList,否则会形成循环。 - 需要监听集合的动态变化:如果ViewModel里的集合是支持通知的类型,当你执行
Add/Clear操作时,需要同步到ListView的选中项,而不仅仅是依赖属性值变化的时候。
修改后的SrListView核心代码如下:
public static readonly DependencyProperty SelectedItemsListProperty = DependencyProperty.Register( nameof(SelectedItemsList), typeof(IList), typeof(SrListView), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsListChanged)); private IList _previousSelectedItemsList; private bool _isUpdatingFromViewModel; public IList SelectedItemsList { get => (IList)GetValue(SelectedItemsListProperty); set => SetValue(SelectedItemsListProperty, value); } private static void OnSelectedItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is SrListView listView) { // 取消订阅之前集合的变化事件 if (listView._previousSelectedItemsList is INotifyCollectionChanged oldCollection) { oldCollection.CollectionChanged -= listView.OnSelectedItemsListCollectionChanged; } // 订阅新集合的变化事件 if (e.NewValue is INotifyCollectionChanged newCollection) { newCollection.CollectionChanged += listView.OnSelectedItemsListCollectionChanged; } listView._previousSelectedItemsList = e.NewValue as IList; // 同步初始选中项 listView.UpdateSelectedItemsFromViewModel(); } } private void OnSelectedItemsListCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { UpdateSelectedItemsFromViewModel(); } private void UpdateSelectedItemsFromViewModel() { if (SelectedItemsList == null) return; _isUpdatingFromViewModel = true; try { SelectedItems.Clear(); foreach (var item in SelectedItemsList) { // 确保item在ItemsSource中存在,否则无法选中 if (Items.Contains(item)) { SelectedItems.Add(item); } } } finally { _isUpdatingFromViewModel = false; } } private void SrListView_SelectionChanged(object sender, SelectionChangedEventArgs e) { // 只有当变化来自UI时,才同步到ViewModel if (!_isUpdatingFromViewModel && SelectedItemsList != null) { SelectedItemsList.Clear(); foreach (var item in SelectedItems) { SelectedItemsList.Add(item); } } }
2. 优化ViewModel中的集合类型
你当前用的是IList,如果直接用ArrayList或者普通List,当你调用Clear()/Add()时,不会触发INotifyCollectionChanged,也就无法通知ListView同步。建议改用强类型的ObservableCollection<T>,这样集合的变化会自动触发UI更新:
// 把T替换成你的Package实体类型,比如DTO_Package private ObservableCollection<DTO_Package> _ilSelectedPackages = new ObservableCollection<DTO_Package>(); public ObservableCollection<DTO_Package> ilSelectedPackages { get => _ilSelectedPackages; set { if (_ilSelectedPackages != value) { _ilSelectedPackages = value; OnPropertyChanged(nameof(ilSelectedPackages)); } } }
3. XAML绑定调整
确保你的ListView的SelectionMode设置正确(已经是Extended,没问题),绑定可以去掉UpdateSourceTrigger,因为双向绑定默认会在合适的时机更新:
<CustomListView SelectionMode="Extended" ItemsSource="{Binding ocPackages}" SelectedItem="{Binding objSelectedPackage}" SelectedItemsList="{Binding ilSelectedPackages, Mode=TwoWay}"> </CustomListView>
关键注意点
- 确保你要选中的
objDTO_PackageToSelect确实存在于ocPackages集合中,否则ListView无法找到该item进行选中。 - 如果你的
ocPackages是ObservableCollection,当集合内容变化时,要确保选中项的同步逻辑正常。
这样修改后,你在ViewModel中执行:
ilSelectedPackages.Clear(); ilSelectedPackages.Add(objDTO_PackageToSelect);
ListView就会自动选中对应的项了。
内容的提问来源于stack exchange,提问作者Le ZVince
相关产品推荐
相关产品推荐

