我可以将关键笔划等事件传递给Silverlight中的另一个控件吗?

时间:2010-03-11 18:22:27

标签: silverlight controls

我可以将关键笔划等事件传递给 Silverlight 中的另一个控件吗?

想象一下,我在一个包含TextboxTreeview的自定义控件中。

我正在收听Key的{​​{1}}事件。当用户按下向上箭头向下箭头键时,我希望TextBox的行为就像它本身接收到该事件一样,即它应该移动当前选择向上或向下。用户不应该忽视Treeview,以便他们可以继续输入。

这可能吗?我不想在Treeview上手动设置选择,因为它没有简单的 MoveSelectionUp() MoveSelectionDown()方法,所以我必须复制那个功能并不是那么微不足道,特别是当树是数据绑定并按需加载节点时。

3 个答案:

答案 0 :(得分:1)

也许你可以考虑使用Mediator Design Pattern来实现这个目标吗?

答案 1 :(得分:1)

我只能想到两种方法来“转发”关键事件:

您的方式:在您自己的代码中注册关键事件处理程序和处理事件,并通过propertiesmethodsextensions(以及API对目标控件执行状态操作目标控件提供)。 这是有效的,但它基本上是重新实现别人已经编写的东西 - 在目标控件内 - 并且你总是冒着忘记角落案例的风险。

继承目标控件并将输入文本框添加到ControlTemplate: 当您的控件与现有控件非常相似时,您可以诚实地说“MyFoo IS_A SomeTargetControl”(然后无论如何,您可能希望提供DependencyProperties进行进一步的自定义,这只是已经复制的内容出现在目标控件类中)你应该完全使用继承。 您的TextBox不会将ArrowUpArrowDown个关键事件设置为handled,因此继承的密钥处理代码会处理它们,并会相应地操纵选择。< / p>

答案 2 :(得分:0)

与此同时,我通过在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;
    }
}