如何将控件中的关键事件冒泡到窗体?

3
我知道一个Windows窗体的KeyPreview属性,它允许窗体在键事件被传递给焦点控件之前接收这些事件。但是,我希望在焦点控件处理完事件后,窗体再接收到这个事件。
我在窗体上放了一个文本框来测试。在文本框中输入时,通过按特定的键命令(如Ctrl-S、F1等),它应该执行默认行为。但是,我希望这些命令能够通过文本框向上冒泡到窗体上,以便在更高层次上处理它们。这些命令是文本框默认不处理的。
然而,我确实需要事件先经过文本框。需要使用这种功能的应用程序比这个简单的示例复杂得多。例如,在文本框成为焦点控件时,它应该使用Ctrl-C和Ctrl-V执行默认的复制和粘贴。但是,在其他各种控件拥有焦点时,这些命令需要传递到最顶层的窗体级别进行处理。
编辑:看起来输入事件从窗体传递到焦点控件,而不是像我期望的那样从焦点控件传递到窗体。如果它从焦点传递到窗体,我可能不会遇到我目前的问题。
编辑2:阅读了这篇文章之后:http://www.codeproject.com/KB/WPF/BeginWPF3.aspx,我现在认为我所期望的‘冒泡’方式只在WPF中可用,而不是标准的C#。我认为我需要重新考虑用户与我的应用程序交互的方式,而不是编写大量丑陋的代码。
能够以不丑陋的方式在C#中执行WPF-style的冒泡的人将获得重要的奖励点数。
4个回答

2

您仍然可以使用KeyPreview属性,但需要检查哪个控件被聚焦,如果是文本框,则不执行任何操作;否则,如果是另一个控件(例如RichTextBox),则处理按下的键。为了获取当前聚焦的控件,您可能需要访问Win32 API。

例如:创建一个新的Windows表单应用程序,在表单中添加一个文本框和一个RichTextBox,将表单的KeyPreview属性设置为true,为表单、文本框和RichTextBox添加KeyDown事件的事件处理程序,以及以下using语句:

using System.Runtime.InteropServices;//for DllImport

然后,将表单的代码替换为以下代码:
public partial class Form1 : Form
{
    // Import GetFocus() from user32.dll
    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)]
    internal static extern IntPtr GetFocus();

    protected Control GetFocusControl()
    {
        Control focusControl = null;
        IntPtr focusHandle = GetFocus();
        if (focusHandle != IntPtr.Zero)
            // returns null if handle is not to a .NET control
            focusControl = Control.FromHandle(focusHandle);
        return focusControl;
    } 

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        Control focusedControl = GetFocusControl();
        if (focusedControl != null && !(focusedControl is TextBox) && e.Control && e.KeyCode == Keys.C)//not a textbox and Copy
        {
            MessageBox.Show("@Form");
            e.Handled = true;
        }
    }

    private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
    {
        if(e.Control && e.KeyCode == Keys.C)
            MessageBox.Show("@Control");
    }

    private void textBox1_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Control && e.KeyCode == Keys.C)
            MessageBox.Show("@Control");
    }
}

目前为止最佳答案;我会试着用一下看看效果如何。干杯。 - Wex
似乎符合我的需求。顺便提一下,Control.FromHandle(focusHandle) 在 ToolStripComboBox 和 PropertyGrid 的字段上似乎会返回 null。可能还有其他情况,但对于我的应用程序来说这不是什么大问题。 - Wex

0

这里使用了KeyPreview属性;但我不想这样做,因为有很多控件需要覆盖KeyPress处理程序。 - Wex

0

只有在KeyPreview属性为true时,才会调用Form.KeyDown。如果我使用KeyPreview,那么Ctrl-C和Ctrl-V将被窗体捕获、处理,并且永远不会到达TextBox。[几分钟后……] 实际上,我刚试了一下,将Handled设置为true并不能阻止命令到达TextBox。SuppressKeyPress可以阻止它……但这并没有解决问题。 - Wex

-1

您可以为文本框的KeyPress和/或KeyUp/KeyDown事件实现事件处理程序。

在KeyPress事件的事件处理程序中,如果您将事件参数上的Handled属性设置为true,则该事件将不会传递到文本框。如果您没有将其设置为true,则会传递。

(编辑以澄清第二段)。


没问题,但事件不会继续冒泡以被表单捕获。 - Wex
我不确定你在问什么。我创建了一个Windows窗体应用程序,在窗体中添加了一个文本框,并为文本框的KeyPress事件添加了一个事件处理程序。然后,窗体就是处理文本框KeyPress事件的对象。这里没有必须发生的"冒泡"。 - Adam Lear
嗯,最简单的说法就是我希望冒泡发生。 :/ - Wex

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