如何让儿童窗口将钥匙送回

本文关键字:钥匙 窗口 | 更新日期: 2023-09-27 17:59:40

我几乎肯定错过了一些非常明显的东西。我有一个带有快捷方式的各种菜单项的单例主窗体。我有一些子窗口,它们可以嵌入到主窗体上,也可以自己浮动。主窗体类有一个指向一个现存主窗体的静态成员,因此子窗口可以访问其公共函数。我希望通常链接到工具栏条目的热键可以在子窗口中工作,并且我不希望重复代码。我知道我必须在主窗体上调用一个按键事件,但我今天运行的是完全空白的。

举一个简单的例子,有一些菜单项可以保存当前文件,并将窗口集中在鼠标所在的点上,分别由Ctrl+SCtrl+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事件,以实现最大程度的偏执,但我想我会尝试找到一种方法,让主窗体发出信号,表明它改变了菜单结构。