You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 04:48:03