使用MVVM模式无法通过Xceed CheckListBox获取SelectedItems
我之前也踩过Xceed CheckListBox这个SelectedItems绑定的坑,正好可以给你几个实用的解决办法:
首先得搞清楚问题根源:Xceed CheckListBox的SelectedItems是一个只读的ICollection——当用户勾选/取消勾选选项时,它只会修改这个集合的内容,而不会重新给这个集合赋值。这就是你的ViewModel属性setter始终不触发的原因,绑定系统没有给你的属性赋新值,只是在原集合里增删元素而已。
方案1:用ObservableCollection监听集合变化(最推荐)
在ViewModel里声明一个ObservableCollection<你的枚举类型>,绑定到CheckListBox的SelectedItems,然后通过监听集合的CollectionChanged事件来捕获选中项的变化,不用依赖属性setter。
ViewModel代码示例(假设你的枚举是MyEnum):
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Collections.Specialized; using System.Linq; public class YourViewModel : INotifyPropertyChanged { // 枚举数据源:把枚举值转成泛型集合 public IEnumerable<MyEnum> EnumOptions { get; } = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>(); private ObservableCollection<MyEnum> _selectedEnums; public ObservableCollection<MyEnum> SelectedEnums { get => _selectedEnums; set { if (_selectedEnums != value) { // 移除旧集合的事件监听 if (_selectedEnums != null) _selectedEnums.CollectionChanged -= OnSelectedEnumsChanged; _selectedEnums = value; OnPropertyChanged(); // 给新集合添加监听 if (_selectedEnums != null) _selectedEnums.CollectionChanged += OnSelectedEnumsChanged; } } } public YourViewModel() { // 初始化空集合 SelectedEnums = new ObservableCollection<MyEnum>(); } private void OnSelectedEnumsChanged(object sender, NotifyCollectionChangedEventArgs e) { // 这里处理选中项变化的逻辑 // 比如获取当前所有选中项:var currentSelected = SelectedEnums.ToList(); Console.WriteLine($"选中项更新:当前共选中{SelectedEnums.Count}个选项"); } // 实现INotifyPropertyChanged接口 public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
XAML代码示例:
<!-- 记得先引入Xceed的命名空间 --> <UserControl xmlns:xceed="http://schemas.xceed.com/wpf/xaml/toolkit"> <xceed:CheckListBox ItemsSource="{Binding EnumOptions}" SelectedItems="{Binding SelectedEnums, Mode=TwoWay}" DisplayMemberPath="ToString()"> <!-- 要是需要自定义选项样式,可以在这里写ItemTemplate --> </xceed:CheckListBox> </UserControl>
方案2:用Xceed自带的SelectedItemsBinding(版本兼容的话更简洁)
部分新版本的Xceed CheckListBox提供了SelectedItemsBinding属性,专门用于双向绑定选中项集合,用法更直接:
XAML代码示例:
<xceed:CheckListBox ItemsSource="{Binding EnumOptions}" SelectedItemsBinding="{Binding SelectedEnums, Mode=TwoWay}" DisplayMemberPath="ToString()" />
ViewModel代码和方案1完全一致,你依然可以通过CollectionChanged监听变化,或者在需要的时候直接访问SelectedEnums集合获取当前选中项。
方案3:手动同步的附加行为(兜底方案)
如果上面两种方法都不适合你的场景,可以用WPF附加行为手动监听SelectionChanged事件,同步选中项到ViewModel:
附加行为代码:
using System.Windows; using System.Windows.Controls; using Xceed.Wpf.Toolkit; public static class CheckListBoxSelectionBehavior { public static readonly DependencyProperty BoundSelectedItemsProperty = DependencyProperty.RegisterAttached( "BoundSelectedItems", typeof(IList), typeof(CheckListBoxSelectionBehavior), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnBoundSelectedItemsChanged)); public static IList GetBoundSelectedItems(DependencyObject obj) { return (IList)obj.GetValue(BoundSelectedItemsProperty); } public static void SetBoundSelectedItems(DependencyObject obj, IList value) { obj.SetValue(BoundSelectedItemsProperty, value); } private static void OnBoundSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is CheckListBox checkListBox) { checkListBox.SelectionChanged -= OnCheckListBoxSelectionChanged; if (e.NewValue is IList newSelectedList) { checkListBox.SelectionChanged += OnCheckListBoxSelectionChanged; // 初始化选中项 checkListBox.SelectedItems.Clear(); foreach (var item in newSelectedList) { checkListBox.SelectedItems.Add(item); } } } } private static void OnCheckListBoxSelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender is CheckListBox checkListBox) { var boundList = GetBoundSelectedItems(checkListBox); if (boundList != null) { // 同步选中项到ViewModel的集合 boundList.Clear(); foreach (var item in checkListBox.SelectedItems) { boundList.Add(item); } } } } }
XAML使用示例:
<xceed:CheckListBox ItemsSource="{Binding EnumOptions}" local:CheckListBoxSelectionBehavior.BoundSelectedItems="{Binding SelectedEnums}" DisplayMemberPath="ToString()" />
这个方案里,ViewModel的SelectedEnums可以是普通的List<MyEnum>或者ObservableCollection<MyEnum>,选中项变化时会自动同步。
最后几个注意点:
- 一定要确保你的ViewModel实现了
INotifyPropertyChanged接口,否则绑定不会生效。 - 枚举作为数据源时,记得用
Cast<你的枚举类型>把Enum.GetValues返回的Array转成泛型集合,避免绑定时的类型问题。 - 确认引用的Xceed.Wpf.Toolkit程序集版本是兼容的,太旧的版本可能没有
SelectedItemsBinding属性。
内容的提问来源于stack exchange,提问作者Shweta Goyal

