Win Forms UserControl无法检测到按键操作

5
有很多关于这个问题的疑问(一个),但我尝试了它们所有的修复方法,它们要么不起作用,要么不适合我的目的。这是我的基本结构:
User Control
|-Panel
  |-Picture Box (several of them, created at runtime, do not exist at design time)

因为我认为这很相关,面板的停靠设置为“填充”,大小调整设置为“增长和缩小”,所以它总是覆盖整个用户控件。PictureBoxes总是覆盖部分面板,但通常不是全部(尽管可能)。
我特别听取Ctrl + C的输入,并且需要一种方法,无论哪个子元素获得焦点都能响应。我想要一种可以侦听任意按键的方法,以便稍后进行扩展。
链接页面上的答案之一建议为这些按键设置全局侦听器,但我不想这样做,因为如果它是后台应用程序,则不希望其触发。另一个建议在顶级窗体中检测它并将其过滤到我的用户控件。问题在于用户控件下正在构建DLL,我不想强制使用它的应用程序来实现对Ctrl + C的侦听,这是应该自己处理的事情。
为什么上面的链接对我不起作用
1)我没有KeyPreview属性可设置为true在我的用户控件上。该问题的第二个答案建议重写ProcessCmdKey,我已经这样做了,但是无论我尝试什么,回调都不会被调用。
2)这也建议重写ProcessCmdKey。正如我所说,它从来没有被调用过。
3)没有接受按钮可以设置为true。
4)KeyDown和PreviewKeyDown回调均已实现,但从未被调用。
5)还建议使用ProcessCmdKey。
如何在用户控件级别检测键事件而不考虑焦点?或者,如果我尝试的上述方法应该起作用,那么我错过了哪些设置阻止它起作用?
2个回答

4

OP: 我需要监听 Ctrl + C 快捷键,而且需要一种方法能够响应不管哪个子元素拥有焦点。

如果你想处理一个类似于 Ctrl+C 的快捷键组合,即使你的控件没有焦点或无法选择,你可以在用户控件中添加一个不可见的 MenuStrip,并添加一个菜单项并将快捷键分配给它。然后处理该项的点击事件并执行所需操作。

每次用户按下 Ctrl+C 快捷键时,点击事件都会被触发,即使你的控件没有焦点。

你也可以使用代码实现:

public UserControl1()
{
    InitializeComponent();
    var menu = new MenuStrip();
    var item = new ToolStripMenuItem();
    item.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C;
    item.Click += item_Click;
    menu.Items.Add(item);
    menu.Visible = false;
    this.Controls.Add(menu);
}
void item_Click(object sender, EventArgs e)
{
    MessageBox.Show("Ctrl + C");
}

注意

如果没有焦点,您将无法处理关键事件,但是使用MenuStrip,您可以使用上述方法捕获所需的快捷键。

能够正常工作的原因是,FormContainerControl,而ContainerControlProcessCmdKey中调用ToolStripManager.ProcessCmdKey方法,从而处理ToolStripManager的所有非上下文菜单条的快捷方式。
有关更多信息,请查看ContainerControl.ProcessCmdKey源代码


这似乎是我见过的最不破解的方法。如果我最终按照你的方式实现它,我会接受这个方法。 - Cody
没问题。你可能会觉得它有用 :) - Reza Aghaei
使其正常工作的原因是,Form 是一个 ContainerControl 并且 ContainerControl 在其 ProcessCmdKey 中调用 ToolStripManager.ProcessCmdKey 方法,导致处理 ToolStripManager 的所有非上下文菜单条的快捷方式。 有关详细信息,请查看 ContainerControl.ProcessCmdKey源代码 - Reza Aghaei
如果您想在控件没有焦点的情况下捕获快捷键,我认为这是最好的解决方法。 - Reza Aghaei
1
今天我的投票用完了,抱歉。我会尽快处理的。 - Cody

3
键盘事件会在具有焦点的控件上触发。您选择的控件不喜欢获得焦点,不显示焦点,并且对按键本身没有用处。这确实引出了一个问题,即您的应用程序用户怎么可能知道Ctrl+C将要做什么。
我假设Ctrl+C应该复制PictureBox中的图像到剪贴板。所以最好的方法是从PB派生自己的类并修改它,使其可以被选择并显示焦点。在项目中添加一个新类并粘贴下面显示的代码。编译。从工具箱的顶部拖动它,替换您用户控件中的PB。
using System;
using System.Windows.Forms;
using System.Drawing;

class SelectablePictureBox : PictureBox {
    public SelectablePictureBox() {
        this.SetStyle(ControlStyles.Selectable, true);
        this.TabStop = true;
    }
    protected override void OnMouseDown(MouseEventArgs e) {
        if (e.Button == MouseButtons.Left) this.Focus();
        base.OnMouseDown(e);
    }
    protected override void OnEnter(EventArgs e) {
        this.Invalidate();
        base.OnEnter(e);
    }
    protected override void OnLeave(EventArgs e) {
        this.Invalidate();
        base.OnLeave(e);
    }
    protected override void OnPaint(PaintEventArgs e) {
        base.OnPaint(e);
        if (this.Focused) {
            var rc = this.DisplayRectangle;
            rc.Inflate(new Size(-2, -2));
            ControlPaint.DrawFocusRectangle(e.Graphics, rc);
        }
    }
}

首先,让我说一下用户会知道 Ctrl+C 的作用,但我不能在这里透露(因为保密协议等原因),但在上下文中它是有意义的。此外,他们并不是复制图像,而是其中的文本。我已经可以扫描图像以获取文本,并且已经加载了包含该信息的字符串。在 Word 中,您可以突出显示一些文本,无论鼠标在窗口上的哪个位置(或窗口外),Ctrl+C 都会将其复制。我需要获得该功能,同时(最好)不使用全局侦听器来捕获应用程序不处于活动状态时的操作。 - Cody
1
嗯,那个不是很相关的,只需要使用这个控件,并重写OnKeyDown()方法来实现你想要做的任何操作。 - Hans Passant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接