WPF Multiselect TreeView - почти полностью реализован, но - PullRequest
0 голосов
/ 01 апреля 2011

Я написал свой собственный "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 ... но оно работает.

1 Ответ

0 голосов
/ 01 апреля 2011

Возможно, он не ответит на все ваши вопросы, но все же:

, что кажется невозможным найти ItemsControl, соответствующий элементу данных без (в худшем случае) расширения всего дерева

Насколько я понял, ваше намерение ItemContainerGenerator.ContainerFromItem должно вам помочь.Возвращает TreeViewItem для данного элемента данных.

...