Можно ли передавать такие события, как нажатия клавиш, в другой элемент управления в Silverlight? - PullRequest
1 голос
/ 11 марта 2010

Можно ли передавать события, например нажатия клавиш, в другой элемент управления в Silverlight ?

Представьте, что я нахожусь в пользовательском элементе управления, который содержит Textbox и Treeview.

Я слушаю Key событие для TextBox. Когда пользователь нажимает клавишу Стрелка вверх или Стрелка вниз , я хочу, чтобы Treeview вел себя так, как если бы он сам получил это событие, т.е. он должен переместить текущий выбор вверх или вниз. Пользователь не должен терять фокус на TextBox, чтобы он мог продолжать печатать.

Возможно ли это? Я не хочу устанавливать выделение вручную в древовидном представлении, поскольку в нем нет простого MoveSelectionUp () или MoveSelectionDown () , поэтому мне придется дублировать эту функциональность, которая так тривиально, особенно когда дерево привязано к базе данных и загружает узлы по требованию.

Ответы [ 3 ]

1 голос
/ 27 марта 2014

Я могу думать только о двух способах «пересылки» ключевых событий:

Ваш путь: регистрируйте ключевые обработчики событий и обрабатывайте события в своем собственном коде и выполняйте манипуляции с состоянием на целевом элементе управления через properties, methods, extensions (и все, что API предлагает целевой элемент управления ). Это работает, но в основном это повторяющиеся вещи, которые кто-то уже написал - внутри целевого элемента управления - и вы всегда рискуете забыть угловой случай.

Наследуйте от целевого элемента управления и добавьте текстовое поле ввода в ControlTemplate: Когда ваш элемент управления действительно очень похож на существующий элемент управления, поэтому вы можете честно сказать «MyFoo IS_A SomeTargetControl» (и тогда в любом случае есть вероятность, что вы захотите предложить DependencyProperties для дальнейшей настройки, которая является лишь копией того, что уже присутствует в целевой класс управления) вы должны полностью использовать наследование. Ваш TextBox не будет устанавливать ключевые события ArrowUp и ArrowDown равными handled, поэтому унаследованный код обработки ключей позаботится о них и будет корректно манипулировать выбором.

1 голос
/ 12 марта 2010

Может быть, вы могли бы использовать Шаблон проектирования посредника для достижения этой цели?

0 голосов
/ 12 марта 2010

Тем временем я решил свой особый сценарий, создав методы расширения MoveSelectionUp() и MoveSelectionDown() для TreeView. Я скопировал реализацию из некоторых закрытых методов в контрольном коде Toolkit и внес небольшие изменения при доступе к закрытым или защищенным методам. Благодаря всем методам расширений, которые доступны в наборе инструментов, теперь это не так сложно.

Поскольку это в основном не мое, я приведу нижеприведенный код, если будущие посетители столкнутся с той же проблемой.

Однако я бы оставил этот вопрос открытым, потому что мне все же хотелось бы узнать в более общем виде, можно ли переадресовывать события в рамках DependencyObject.

TreeViewExtensions :

using System;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

static public class TreeViewExtensions
{
    static public void SetSelectedContainerIfValid(this TreeView self, TreeViewItem itm)
    {
        Contract.Requires(self != null);

        if (itm != null)
        {
            self.SetSelectedContainer(itm);
        }
    }

    static public void MoveSelectionUp(this TreeView self)
    {
        Contract.Requires(self != null);

        var itm = self.GetSelectedContainer();
        if (itm == null)
        {
            self.SetSelectedContainerIfValid(self.GetContainers().LastOrDefault());
        }
        else
        {
            self.SetSelectedContainerIfValid(itm.GetContainerAbove());
        }
    }

    static public void MoveSelectionDown(this TreeView self)
    {
        Contract.Requires(self != null);

        var itm = self.GetSelectedContainer();
        if (itm == null)
        {
            self.SetSelectedContainerIfValid(self.GetContainers().FirstOrDefault());
        }
        else
        {
            self.SetSelectedContainerIfValid(itm.GetContainerBelow());
        }
    }
}

TreeViewItemExtensions

using System;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;

static public class TreeViewItemExtensions
{
    static public TreeViewItem GetContainerBelow(this TreeViewItem self)
    {
        return TreeViewItemExtensions.GetContainerBelow(self, true);
    }

    /// <summary>
    /// Find the next focusable TreeViewItem below this item.
    /// </summary>
    /// <param name="recurse">
    /// A value indicating whether the item should recurse into its child
    /// items when searching for the next focusable TreeViewItem.
    /// </param>
    /// <returns>The next focusable TreeViewItem below this item.</returns>
    static public TreeViewItem GetContainerBelow(this TreeViewItem self, bool recurse)
    {
        Contract.Requires(self != null);

        // Look for the next item in the children of this item (if allowed)
        if (recurse && self.IsExpanded && self.HasItems)
        {
            TreeViewItem item = self.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
            if (item != null)
            {
                return item.IsEnabled ?
                    item :
                    item.GetContainerBelow(false);
            }
        }

        // Look for the next item in the siblings of this item
        ItemsControl parent = self.GetParentTreeViewItem() as ItemsControl ?? self.GetParentTreeView();
        if (parent != null)
        {
            // Get the index of this item relative to its siblings
            TreeViewItem item = null;
            int index = parent.ItemContainerGenerator.IndexFromContainer(self);
            int count = parent.Items.Count;

            // Check for any siblings below this item
            while (index++ < count)
            {
                item = parent.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
                if (item != null && item.IsEnabled)
                {
                    return item;
                }
            }

            // If nothing else was found, try to find the next sibling below
            // the parent of this item
            TreeViewItem parentItem = self.GetParentTreeViewItem();
            if (parentItem != null)
            {
                return parentItem.GetContainerBelow(false);
            }
        }

        return null;
    }

    /// <summary>
    /// Find the last focusable TreeViewItem contained by this item.
    /// </summary>
    /// <returns>
    /// The last focusable TreeViewItem contained by this item.
    /// </returns>
    static public TreeViewItem GetLastContainer(this TreeViewItem self)
    {
        Contract.Requires(self != null);

        TreeViewItem item = self;
        TreeViewItem lastItem = null;
        int index = -1;

        // Walk the children of the current item
        while (item != null)
        {
            // Ignore any disabled items
            if (item.IsEnabled)
            {
                // If the item has no children, it must be the last
                if (!item.IsExpanded || !item.HasItems)
                {
                    return item;
                }

                // If the item has children, mark it as the last known
                // focusable item so far and walk into its child items,
                // starting from the last item and moving toward the first
                lastItem = item;
                index = item.Items.Count - 1;
            }
            else if (index > 0)
            {
                // Try searching for the previous item's sibling
                index--;
            }
            else
            {
                // Stop searching if we've run out of children
                break;
            }

            // Move to the item's previous sibling
            item = lastItem.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
        }

        return lastItem;
    }

    /// <summary>
    /// Find the previous focusable TreeViewItem above this item.
    /// </summary>
    /// <returns>
    /// The previous focusable TreeViewItem above this item.
    /// </returns>
    static public TreeViewItem GetContainerAbove(this TreeViewItem self)
    {
        Contract.Requires(self != null);

        ItemsControl parent = self.GetParentTreeViewItem() as ItemsControl ?? self.GetParentTreeView();
        if (parent == null)
        {
            return null;
        }

        // Get the index of the current item relative to its siblings
        int index = parent.ItemContainerGenerator.IndexFromContainer(self);

        // Walk the previous siblings of the item to find a focusable item
        while (index-- > 0)
        {
            // Get the sibling
            TreeViewItem item = parent.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
            if (item != null && item.IsEnabled)
            {
                // Get the last focusable descendent of the sibling
                TreeViewItem last = item.GetLastContainer();
                if (last != null)
                {
                    return last;
                }
            }
        }

        return parent as TreeViewItem;
    }
}
...