我可以将关键笔划等事件传递给 Silverlight 中的另一个控件吗?
想象一下,我在一个包含Textbox
和Treeview
的自定义控件中。
我正在收听Key
的{{1}}事件。当用户按下向上箭头或向下箭头键时,我希望TextBox
的行为就像它本身接收到该事件一样,即它应该移动当前选择向上或向下。用户不应该忽视Treeview
,以便他们可以继续输入。
这可能吗?我不想在Treeview上手动设置选择,因为它没有简单的 MoveSelectionUp()或 MoveSelectionDown()方法,所以我必须复制那个功能并不是那么微不足道,特别是当树是数据绑定并按需加载节点时。
答案 0 :(得分:1)
也许你可以考虑使用Mediator Design Pattern来实现这个目标吗?
答案 1 :(得分:1)
我只能想到两种方法来“转发”关键事件:
您的方式:在您自己的代码中注册关键事件处理程序和处理事件,并通过properties
,methods
,extensions
(以及API
对目标控件执行状态操作目标控件提供)。
这是有效的,但它基本上是重新实现别人已经编写的东西 - 在目标控件内 - 并且你总是冒着忘记角落案例的风险。
继承目标控件并将输入文本框添加到ControlTemplate
:
当您的控件与现有控件非常相似时,您可以诚实地说“MyFoo IS_A SomeTargetControl”(然后无论如何,您可能希望提供DependencyProperties
进行进一步的自定义,这只是已经复制的内容出现在目标控件类中)你应该完全使用继承。
您的TextBox
不会将ArrowUp
和ArrowDown
个关键事件设置为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;
}
}