如何让儿童窗口将钥匙送回
本文关键字:钥匙 窗口 | 更新日期: 2023-09-27 17:59:40
我几乎肯定错过了一些非常明显的东西。我有一个带有快捷方式的各种菜单项的单例主窗体。我有一些子窗口,它们可以嵌入到主窗体上,也可以自己浮动。主窗体类有一个指向一个现存主窗体的静态成员,因此子窗口可以访问其公共函数。我希望通常链接到工具栏条目的热键可以在子窗口中工作,并且我不希望重复代码。我知道我必须在主窗体上调用一个按键事件,但我今天运行的是完全空白的。
举一个简单的例子,有一些菜单项可以保存当前文件,并将窗口集中在鼠标所在的点上,分别由Ctrl+S
和Ctrl+E
触发。在我的主窗体中,它们被设置为键盘快捷键,但实际的繁重工作是由后一个命令的子窗口完成的。我通过在孩子窗口中捕捉KeyDown事件暂时修复了它,但这意味着同一快捷方式会出现在两个不同的地方。
处理快捷键的解决方案似乎很少。其中之一可能是按照这里的建议安装键盘挂钩。你也可以尝试通过添加自定义消息过滤器来处理这个问题,正如这里所建议的,但是,我还没有验证发布在那里的代码。
使用钩子的解决方案似乎有点棘手,所以您可能想先尝试自定义消息过滤器。
根据我对上述Lukasz M的评论,由于遗留的原因,我需要维护当前的菜单结构,即所有工程师都习惯于更改菜单,他们希望菜单快捷方式自动工作。我可能会使用自定义消息过滤器修改中央热键位置,为菜单项生成快捷方式文本,但这会增加额外的复杂性,下次有人加入快速菜单项时可能会取消。因此,我采用了我在评论中提到的解决方案,为子窗口提供了一个不可见的菜单。
令我惊讶的是,如果一个菜单的Visible
属性设置为false,那么热键就可以正常工作。如果从子窗体调用,则与主窗体中的菜单项相关联的事件可以很好地工作,因为它们是相对于定义它们的窗口而不是由调用它们的窗口执行的。因此,我的第一个解决方案是从主窗体中获取MenuStrip
并将其添加到子窗体中。这并不奏效,因为在子窗体中使其不可见也会使其在主窗体中不可见。我的下一次尝试是创建一个新的隐藏MenuStrip
,并将主窗体的MenuStrip
中的ToolStripMenuItem
项添加到它上面。这打破了热键功能,可能是因为菜单项现在存在于多个地方。最后,我创建了菜单项的浅层副本,其中只包含快捷键、Tag
属性(对于使用它的几个菜单项来说是必要的)和事件处理程序(最后一个是通过"如何在运行时克隆控制事件处理程序?"中描述的方法完成的)。经过一番摆弄,我意识到我不需要维护菜单的结构,我只需要有快捷键的项目。这就是我最终得到的:
主窗体:
/// <summary>
/// Returns copies of all menu shortcut items in the main form.
/// </summary>
/// <returns>A list containing copies of all of the menu items with a keyboard shortcut.</returns>
public static List<ToolStripMenuItem> GetMenuShortcutClones()
{
List<ToolStripMenuItem> shortcutItems = new List<ToolStripMenuItem>();
Stack<ToolStripMenuItem> itemsToBeParsed = new Stack<ToolStripMenuItem>();
foreach (ToolStripItem menuItem in mainForm.menuStrip.Items)
{
if (menuItem is ToolStripMenuItem)
{
itemsToBeParsed.Push((ToolStripMenuItem)menuItem);
}
}
while (itemsToBeParsed.Count > 0)
{
ToolStripMenuItem menuItem = itemsToBeParsed.Pop();
foreach (ToolStripItem childItem in menuItem.DropDownItems)
{
if (childItem is ToolStripMenuItem)
{
itemsToBeParsed.Push((ToolStripMenuItem)childItem);
}
}
if (menuItem.ShortcutKeys != Keys.None)
{
shortcutItems.Add(CloneMenuItem(menuItem));
}
}
return shortcutItems;
}
/// <summary>
/// Returns an effective shortcut clone of a ToolStripMenuItem. It does not copy the name
/// or text, but it does copy the shortcut and the events associated with the menu item.
/// </summary>
/// <param name="menuItem">The MenuItem to be cloned</param>
/// <returns>The newly generated clone.</returns>
private static ToolStripMenuItem CloneMenuItem(ToolStripMenuItem menuItem)
{
ToolStripMenuItem copy = new ToolStripMenuItem();
copy.ShortcutKeys = menuItem.ShortcutKeys;
copy.Tag = menuItem.Tag;
var eventsField = typeof(Component).GetField("events", BindingFlags.NonPublic | BindingFlags.Instance);
var eventHandlerList = eventsField.GetValue(menuItem);
eventsField.SetValue(copy, eventHandlerList);
return copy;
}
子窗体:
private void OnRefresh(object sender, EventArgs e)
{
// Refresh the hiddenShortcutMenu.
List<ToolStripMenuItem> shortcutList = MainForm.GetMenuShortcutClones();
hiddenShortcutMenu.Items.Clear();
hiddenShortcutMenu.Items.AddRange(shortcutList.ToArray());
}
在子窗体的构造函数中,我实例化了hiddenShortcutMenu
,将Visible
设置为false,将其分配给子窗体的控件,并设置事件。最后一个有点麻烦,因为菜单有时会根据上下文而更改,所以我不得不定期刷新它。目前,我将其设置为Paint
事件,以实现最大程度的偏执,但我想我会尝试找到一种方法,让主窗体发出信号,表明它改变了菜单结构。