【问题标题】:C# focus in forms and arrow keysC# 专注于表单和箭头键
【发布时间】:2024-05-01 01:45:03
【问题描述】:

我想做一个游戏的地图编辑器。它是一个程序,将具有 Windows 窗体 UI(如 propertyGrid 来编辑对象的属性),但它也将具有一个面板,将在其上绘制地图。

我想要什么:

当焦点在带有地图的面板上时,我想使用键盘来移动地图(箭头键),添加对象(数字键)等。当焦点不在此面板上时,我想要按钮在 Windows 窗体中正常工作 - 允许在控件等之间切换。

我的表单如下所示:

它有一个 ToolStripControl,它有一个 menuStrip(用于主菜单)和一个 statusStrip(用于状态栏)。在窗体(或工具条控件)的中间,SplitControl 停靠(停靠=填充),它有两个面板。面板 1 有 PanelMap - 一个显示地图的面板,面板 2 有所有其他东西,如属性网格、选项卡控件、按钮等。

我将表单的 KeyPreview 设置为 true,并在表单的 keydown 事件处理程序中处理键盘事件。

现在,如果我将焦点分配给 PanelMap,会发生什么,下次我按下箭头键时,不会触发 KeyDown 事件。一个都没有!即使是应该处理所有事件的表单,因为它具有“KeyPreview”,也不会触发它。当我按下箭头时,PanelMap 失去对 SplitControl 的关注。

好吧,我想,也许 PanelMap 不应该有焦点,让我们把焦点给 SplitControl(如果我在它有焦点时按箭头键,我可以处理它,所以它不会走得更远)。但是,如果在 SplitControl 内部的某些内容中的文本框之类的东西具有焦点,则 SplitControl 无法获得焦点。 .Focus() 什么都不做 - 焦点仍然在拥有它的任何控件中!

为什么它表现得如此奇怪?为什么当面板具有焦点并按下箭头键时 Form 的 KeyDown 不触发?为什么即使 CanFocus = true,当我调用 .Focus() 时,SplitControl 也不会获得焦点?

最终,我如何实现我想要的?有办法吗?

【问题讨论】:

  • 覆盖表单的 ProcessCmdKey() 以吞下击键。如果您没有进一步处理击键,只需返回 false。

标签: c# windows forms controls


【解决方案1】:

我认为您遇到了在您的事件到达导航之前进行按键操作的小部件。我遇到了这个问题,并且这样做了:

    private void RemoveCursorNavigation(Control.ControlCollection controls)
    {
        foreach(Control ctrl in controls)
        {
            ctrl.PreviewKeyDown += new PreviewKeyDownEventHandler(MainWin_PreviewKeyDown);
            RemoveCursorNavigation(ctrl.Controls);
        }
    }

我在主窗体的 Load 处理程序中调用此函数,如下所示:

RemoveCursorNavigation(this.Controls);

在您的 PreviewKeyDown 处理程序中,您需要这样做:

    public void MainWin_PreviewKeyDown(Object sender, PreviewKeyDownEventArgs e)
    {
        switch(e.KeyCode)
        {
            case Keys.Up:
            case Keys.Down:
            case Keys.Left:
            case Keys.Right:
                e.IsInputKey = true;
                break;
            default:
                break;
        }
    }

e.IsInputKey = true; 告诉外界你已经使用了这个事件,不希望它去其他任何地方。

现在您可以在按键进入小部件之前看到它们,并且您不会通过光标键在它们之间导航。

【讨论】:

  • 问题是,它们仍然进入小部件!我尝试做预览键 - 无法在预览键中使事件被抑制或处理,这意味着它将被对象处理......所以会有焦点在表单周围跳跃
  • e.IsInputKey = true; 那是我用的。
  • 好的,谢谢。我的方法对我来说完美无缺(即使有人低估了它),但我会考虑这样做。
  • 降级是因为以您的方式解决问题会导致其他人落后。顺便说一句,我不是投反对票的人。 :)
  • 它不会引起任何问题,而且做起来更简单(您只需添加一个事件处理程序)。此外,使用 TAB 导航,我的方法将允许对象(地图)成为“焦点”,但覆盖对象的 previewkeydown 的方法不会,因为 TAB 导航不会集中在 Panel 对象上。
【解决方案2】:

我找到了这样的答案:

我制作了一个隐藏在面板下的文本框(但已启用且可见)。当我想将焦点“锁定”在 PanelMap 上时,此文本框将获得焦点。即使使用 e.suppress = true 它也具有 onkeydown ,因此文本框永远不会受到任何击键的影响。

粗略的解决方法,但效果很好...典型的 M$ 业务...

【讨论】: