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

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

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

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

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


person herzmeister    schedule 11.03.2010    source источник


Ответы (3)


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

person Page    schedule 11.03.2010
comment
Спасибо, что ответили. Однако у меня нет архитектурной проблемы, я хочу знать, могу ли я активировать некоторые функции в элементе управления Silverlight, к которым у меня нет доступа, поскольку они скрыты в частных методах. Однако он будет выполняться при возникновении события, поэтому у меня вопрос, есть ли какой-нибудь волшебный способ в этой причудливой структуре DependencyObject, который позволил бы мне перенаправить событие от одного элемента управления к другому. Что-то вроде myTextBox.KeyUp += (sender, e) => TreeView.KeyUpEvent.Trigger(myTreeView, e). - person herzmeister; 12.03.2010

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

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

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

person Martin    schedule 27.03.2014

Тем временем я решил свой особый сценарий, создав методы расширения 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;
    }
}
person herzmeister    schedule 12.03.2010