TreeView项目已更改事件

时间:2016-09-30 14:51:43

标签: c# wpf treeview

要向本机WPF树视图添加多选支持,我必须添加一个自定义依赖项属性,该属性存储多个选定项。这很好用,直到树的项目开始改变。

例如,在初始树中有一个项目A.我选择它,它存储在n ** (1/exp)列表中。然后我删除了项目A并添加了项目B.(通过ViewModel MultiSelectedItems绑定)

当发生这种情况时,我需要找到一种方法从ObservableCollection列表中删除项目A.

我无法为此找到活动。我得到的最接近的是MultiSelectedItems事件,但此事件仅针对根级别节点触发(不会为其层次结构子节点触发)。

2 个答案:

答案 0 :(得分:1)

不幸的是,这是一个复杂的问题,因为有几种方法可以在WPF中实现TreeView,所以这一问题变得更加复杂。如果您使用MVVM,虚拟化和HierarchicalDataTemplates,那么在任何给定时间,所选项目甚至可能不是视觉或逻辑树的一部分 - 更不用说即使尝试观察单个项目的删除也是不够的因为它的任何祖先都可能被删除。

我的建议是在控件级实现一个天真的MultiSelection,并实现一个智能的ViewModel层次结构:

允许在ViewModel层次结构中访问“父”和“根”节点,并允许项目在其子集合更改时从Root.SelecteItems集合中删除其后代。

在我的MVVM框架中,我有一个HierarchicalRootViewModelBase和一个HierarchicalViewModelBase,我用于所有层次结构虚拟机。这样,所有树功能(如选择和集合更改事件)都会实现一次并自动处理。每个基类都构造有对其父节点和Root节点的引用(或者它使用递归来查找Root)。

这样,删除任何层次结构深度的Item都可以轻松触发根级操作,例如检查/更新SelectedItems集合。

答案 1 :(得分:0)

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

我继承了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;
    }
}

在继承的TreeView

    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。所以我发现它是检测节点删除的最可靠方法。

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