WPF Multiselect TreeView - 几乎完全实现,但是

时间:2011-04-01 11:05:06

标签: wpf treeview multi-select

我写了自己的“Multiselect”-Treeview。它为所有数据绑定项使用接口ITreeViewItem。如果没有绑定项将实现此接口,则此树视图将无法工作。

public interface ITreeViewItem : INotifyPropertyChanged
{
    bool IsExpanded { get; set; }
    bool IsSelected { get; set; }
    bool IsEnabled { get; set; }
    Visibility Visibility { get; set; }

    ITreeViewItem GetParentTreeViewItem();
}

这是我的第一个问题:有人知道如何不强制执行此接口吗?看起来不干净。我决定实现这个接口的主要原因是,在没有(在最坏的情况下)扩展整个树的情况下,似乎找不到与数据项匹配的ItemsControl。在延迟加载数据的情况下会导致加载所有这些数据。

另一个原因是所选项目背景的着色。如果我想突出显示背景,我需要绑定一些属性。 IsSelected上的正常触发似乎不起作用,因为一次只有一个项可以设置此值(或?)。

关于我在这里问过的最后一个问题WPF TreeViewItem Background给定答案的问题是,默认模板没有默认颜色(首次点击某个项目时颜色会变淡紫色,第二个就是将是蓝色;这是另一个问题)。那么在树视图失去焦点后,如何将所有选定项目的颜色设置为浅灰色或将实际聚焦的项目设置为蓝色?

如果您需要有关我的实施的更多信息,请随时询问。

编辑:@Snowbear JIM编译器:

 internal static bool ExecuteOnTreeViewItem(this TreeView tree, ITreeViewItem dataItem, Action<TreeViewItem> action)
    {
        Stack<ITreeViewItem> pathToRoot = GetPathToRoot(dataItem);
        TreeViewItem firstVisibleItem = tree.SyncTreeLevels(pathToRoot);

        if (firstVisibleItem == null)
            return false; // can't find any item in path which is currently selectable

        if (pathToRoot.Count != 0) // expand the first item if nextLevelItems will follow
            firstVisibleItem.IsExpanded = true;

        ExecuteOnTreeViewItem(tree, pathToRoot, action, firstVisibleItem);
        return true;
    }

    private static void ExecuteOnTreeViewItem(TreeView tree, Stack<ITreeViewItem> pathToRoot, Action<TreeViewItem> action, TreeViewItem treeViewItem)
    {
        if (pathToRoot.Count == 0)
        {
            action(treeViewItem);
            return;
        }

        var nextLevelItem = pathToRoot.Pop();

        if (pathToRoot.Count != 0)
            nextLevelItem.IsExpanded = true; // make children visible to create the item containers

        if (treeViewItem.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
        {
            EventHandler eventHandler = null;

            if (!treeViewItem.IsExpanded)
                treeViewItem.IsExpanded = true;

            eventHandler = delegate
            {
                if (treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.Error
                    || treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                    treeViewItem.ItemContainerGenerator.StatusChanged -= eventHandler;

                if (treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                {
                    var nextTreeViewItem = treeViewItem.ItemContainerGenerator.ContainerFromItem(nextLevelItem) as TreeViewItem;
                    ExecuteOnTreeViewItem(tree, pathToRoot, action, nextTreeViewItem);
                }
            };

            treeViewItem.ItemContainerGenerator.StatusChanged += eventHandler;
        }
        else
        {
            var nextTreeViewItem = treeViewItem.ItemContainerGenerator.ContainerFromItem(nextLevelItem) as TreeViewItem;
            ExecuteOnTreeViewItem(tree, pathToRoot, action, nextTreeViewItem);
        }
    }

    internal static Stack<ITreeViewItem> GetPathToRoot(ITreeViewItem item)
    {
        Stack<ITreeViewItem> items = new Stack<ITreeViewItem>();
        ITreeViewItem parent = item;

        while (parent != null)
        {
            items.Push(parent);
            parent = parent.GetParentTreeViewItem();
        }

        return items;
    }

    /// <summary>
    /// Returns the first item in stack which is visible in tree view. This is used
    /// for the case that the first Item of ITreeViewItem is not the first bound item
    /// </summary>
    /// <param name="tree"></param>
    /// <param name="hierachyItems"></param>
    /// <returns></returns>
    internal static TreeViewItem SyncTreeLevels(this TreeView tree, Stack<ITreeViewItem> hierachyItems)
    {
        TreeViewItem item = null;

        while (item == null && hierachyItems.Count > 0)
            item = tree.ItemContainerGenerator.ContainerFromItem(hierachyItems.Pop()) as TreeViewItem;

        return item;
    }

关于这段代码的坏处是,只有在将继承的TreeViewItem中的调用事件留下后才会执行Action,但它可以正常工作。

1 个答案:

答案 0 :(得分:0)

可能它不会回答你的所有问题,但仍然是:

  

在没有(最坏的情况下)扩展整个树的情况下,似乎无法找到与数据项匹配的ItemsControl

据我所知,ItemContainerGenerator.ContainerFromItem应该帮助你。它返回给定数据项的TreeViewItem