调用SetValue后ViewModel从DependencyProperty获取到null值问题排查
问题根因
- 绑定类型不匹配
ListBox基类的base.SelectedItems返回的是WPF内部实现的System.Windows.Controls.SelectedItemCollection类型,它只实现了非泛型IList接口,和你ViewModel中声明的ObservableCollection<string>不存在兼容转换关系。WPF绑定系统遇到类型转换失败时,会默认向源属性写入null。 - 依赖属性初始值触发
你注册SelectedItemsProperty时设置的默认值为default(IList)也就是null,控件完成绑定初始化的阶段,会先把默认的null值通过OneWayToSource绑定推送给ViewModel的SelectedDogs属性,这是首次触发set拿到null的直接原因。 - 额外隐含问题:你当前的实现逻辑是每次选中变更直接替换依赖属性的集合实例,就算你把ViewModel属性类型改成
IList能正常拿到值,后续选中项变更也只会更新WPF内部集合的元素,不会再次触发ViewModel属性的set方法,你无法通过属性变更通知感知到选中项更新。
修复方案
修改控件实现为同步选中元素到外部绑定的集合,而非直接替换集合实例:
public class BindableMultiSelectListBox : ListBox { public static new readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(nameof(SelectedItems), typeof(IList), typeof(BindableMultiSelectListBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsChanged)); private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not BindableMultiSelectListBox listBox || e.NewValue is not IList targetList) return; // 绑定变更时同步当前选中项到目标集合 listBox.SyncSelectedItemsToTarget(targetList); } public new IList SelectedItems { get => (IList)GetValue(SelectedItemsProperty); set => SetValue(SelectedItemsProperty, value); } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { base.OnSelectionChanged(e); if (SelectedItems == null) return; // 增量更新外部绑定集合的元素 foreach (var removed in e.RemovedItems) { if (SelectedItems.Contains(removed)) SelectedItems.Remove(removed); } foreach (var added in e.AddedItems) { if (!SelectedItems.Contains(added)) SelectedItems.Add(added); } } private void SyncSelectedItemsToTarget(IList target) { target.Clear(); foreach (var item in base.SelectedItems) { target.Add(item); } } }
使用时无需指定OneWayToSource,提前在ViewModel中初始化SelectedDogs即可:
<usercontrols:BindableMultiSelectListBox SelectedItems="{Binding SelectedDogs}" ItemsSource="{Binding Dogs}" SelectionMode="Extended"/>
// ViewModel中提前初始化集合,避免null public ObservableCollection<string> SelectedDogs { get; set; } = new ObservableCollection<string>();
内容的提问来源于stack exchange,提问作者mottosson
相关产品推荐
相关产品推荐

