WPF自定义TreeView控件绑定ViewModel后Items为空问题求解
问题原因
- 引用类型依赖属性默认值共享
你定义Source依赖属性时直接将静态创建的ObservableCollection<TreeItem>作为默认值,WPF依赖属性的默认值是全局共享的引用,所有ViewTree实例会共用同一个集合实例,很容易出现内容被意外修改、清空的问题,导致绑定到ViewModel的TreeSource对应集合为空。 - 绑定更新的调度延迟
给ViewModel的TreeSource赋值后,TreeView的ItemsSource绑定更新属于UI渲染级别的操作,会按WPF的Dispatcher优先级队列延迟执行。你在赋值后的同步代码中直接访问Items时,Dispatcher还没处理绑定更新逻辑,就会得到空集合;断点时因为线程暂停,Dispatcher有足够时间处理更新,所以能看到子项。 - 内部控件DataContext被覆盖
如果你的trvMenu没有显式设置DataContext,而是继承了ViewTree的DataContext,当ViewTree被外部使用时,外部的DataContext会传入覆盖内部的ViewTreeViewModel实例,导致TreeSource绑定失效,Items自然为空。
解决方案
- 修复依赖属性默认值
修改依赖属性定义,去掉静态默认值,改为在每个ViewTree实例的构造函数中单独初始化默认值,避免多个实例共享集合:
// 依赖属性定义,默认值设为null public static readonly DependencyProperty SourceProperty = DependencyProperty.Register ( "Source", typeof (ObservableCollection<TreeItem>), typeof (ViewTree), new FrameworkPropertyMetadata (null, OnSourceChanged) ); public ViewTree() { InitializeComponent(); // 每个实例单独初始化默认Source SetCurrentValue(SourceProperty, new ObservableCollection<TreeItem>()); // 显式设置trvMenu的DataContext,避免被外部上下文覆盖 trvMenu.DataContext = new ViewTreeViewModel(); }
- 调整Items访问时机
如果需要在Source更新后操作TreeView的Items,将访问逻辑放到Dispatcher队列中,等绑定更新完成后再执行:
public void UpdateTreeSource(ObservableCollection<TreeItem> treeSource) { var vm = trvMenu.DataContext as ViewTreeViewModel; vm.TreeSource = treeSource; // 等待绑定更新完成后再访问Items Dispatcher.InvokeAsync(() => { // 此处访问trvMenu.Items可得到正确结果 var items = trvMenu.Items; }, System.Windows.Threading.DispatcherPriority.Loaded); }
- 验证ViewModel属性通知
确认你的ViewTreeViewModel中TreeSource属性继承了Prism的BindableBase,并且set块触发了属性变更通知:
private ObservableCollection<TreeItem> _treeSource; public ObservableCollection<TreeItem> TreeSource { get => _treeSource; set => SetProperty(ref _treeSource, value); }
内容的提问来源于stack exchange,提问作者Ngoc Nam Nguyen
相关产品推荐
相关产品推荐

