C# TreeView项目更改事件

C# TreeView项目更改事件,c#,wpf,treeview,C#,Wpf,Treeview,要向本机WPF树视图添加多选支持,我必须添加一个存储多选项的自定义依赖项属性。在树的项目开始更改之前,这种方法非常有效 例如,在初始树中有一个项目A。我选择了它,它存储在MultiSelectedItems列表中。然后我删除了项目A并添加了项目B。(通过ViewModelObservableCollectionbinding) 发生这种情况时,我需要找到从MultiSelectedItems列表中删除项目a的方法 我找不到与此相关的事件。我得到的最接近的事件是ItemContainerGener

要向本机WPF树视图添加多选支持,我必须添加一个存储多选项的自定义依赖项属性。在树的项目开始更改之前,这种方法非常有效

例如,在初始树中有一个项目A。我选择了它,它存储在
MultiSelectedItems
列表中。然后我删除了项目A并添加了项目B。(通过ViewModel
ObservableCollection
binding)

发生这种情况时,我需要找到从
MultiSelectedItems
列表中删除项目a的方法


我找不到与此相关的事件。我得到的最接近的事件是
ItemContainerGenerator.ItemsChanged
事件,但此事件仅针对根级别节点触发(不针对其层次结构子节点触发)。

不幸的是,这是一个复杂的问题,只是由于在WPF中实现TreeView的方法有多种,使得问题变得更加复杂。如果使用MVVM、虚拟化和
Hierarchy-CalDataTemplates
,则所选项目在任何给定时间都可能不是可视或逻辑树的一部分-更不用说,即使尝试监视单个项目的删除也不够,因为它的任何祖先都可能被删除

我的建议是在控制级别实现简单的多重选择,并实现智能ViewModel层次结构:

允许在ViewModel层次结构中访问“父节点”和“根节点”,并允许项从
根节点中删除其子节点。当其子集合发生更改时,选择items
集合

在我的MVVM框架中,我有一个用于所有层次结构VM的
HierarchycalRootViewModelBase
HierarchycalViewModelBase
。通过这种方式,所有树功能(如选择和集合更改事件)实现一次并自动处理。每个基类都是通过对其父节点和根节点的引用来构造的(或者使用递归来查找根节点)


通过这种方式,在任何层次结构深度删除项目都可以轻松触发根级别的操作,如检查/更新SelectedItems集合。

解决此问题的关键思想是在每个节点内而不是在树级别检测项目更改事件

我继承了
TreeViewItem

public class MultiSelectTreeViewItem : TreeViewItem
{
    object _originalHeader;
    protected override void OnHeaderChanged(object oldHeader, object newHeader)
    {
        base.OnHeaderChanged(oldHeader, newHeader);
        //.NET 4.5 use BindingOperations.DisconnectedSource
        if (newHeader.ToString() != "{DisconnectedItem}")
            _originalHeader = newHeader;
    }

    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);

        if (Header.ToString() == "{DisconnectedItem}" && _originalHeader != null && e.Action == NotifyCollectionChangedAction.Reset)
        {
            //Find the parent Tree View and remove this from MultiSelectedList
        }
    }


    protected override DependencyObject GetContainerForItemOverride()
    {
        return new MultiSelectTreeViewItem();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is MultiSelectTreeViewItem;
    }
}
在树视图中

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new MultiSelectTreeViewItem();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is MultiSelectTreeViewItem;
    }
删除节点后,其标头将设置为名为DisconnectedItem的sentinel对象,而Items Changed事件将为
NotifyCollectionChangedAction.Reset

请注意,如果执行了
List.Clear()
操作,则不会触发
NotifyCollectionChangedAction.Remove
事件,只会触发
NotifyCollectionChangedAction.Reset
事件。因此,我发现它是检测节点移除的最可靠的方法


一个问题是,如果节点尚未呈现(父节点从未展开),则不会触发此事件。

正如您所提到的,这在任何意义上都不是通用的解决方案,并且存在许多不常见的故障情况。我强烈建议你按照我先前的回答去做。@AndrewHanlon你介意举个例子让我更好地理解它吗?