Winforms:仅在添加断点后执行的代码

本文关键字:执行 代码 断点 添加 Winforms | 更新日期: 2023-09-27 18:20:01

我有这个Winforms代码(在底部)。当我运行它时,会出现一个带有"button1"的表单。我点击按钮,它消失了,然后我按下一个键——这应该会触发OnKeyDown,但什么也没发生。我在MessageBox::Show(...)行上添加了一个断点,点击一个键——现在断点被击中,MessageBox就会显示出来,即使我删除了断点,每次按键也会显示MessageBox。

注:

  • 我已经在两台运行Visual Studio 2013的计算机上测试了这一点该项目是一个.Net 4.5 Winforms项目
  • 在开始调试项目后,必须在Visual Studio中设置断点
  • 在未调试的情况下运行项目从不显示MessageBox
  • 根据Sriram的说明,在构造函数中设置this.KeyPreview = true会导致MessageBox始终显示。尽管我仍然不明白为什么设置断点会导致显示该调试会话的MessageBox

public partial class Form1 : Form
{
    public Form1()
    {
        button1 = new Button();
        button1.Location = new Point(197, 13);
        button1.Name = "button1";
        button1.Text = "button1";
        button1.Click += button1_Click;
        Controls.Add(button1);
        Name = "TestForm";
        KeyDown += OnKeyDown;
    }
    private static void OnKeyDown(object sender, KeyEventArgs keyEventArgs)
    {
        MessageBox.Show("This is shown only after a breakpoint is set on this line");
    }
    private void button1_Click(object sender, EventArgs e)
    {
        button1.Visible = false;
    }
    private readonly Button button1;
}

Winforms:仅在添加断点后执行的代码

它与设置断点无关。这种行为是由断点命中时发生的其他事件引起的:窗口被取消激活。在与应用程序相同的计算机上运行调试器是不可避免的副作用。当您恢复跑步时,您的窗口将重新激活。现在,您可以看到Winforms中兼容性代码的副作用,它使UI的行为与前代(VB6)相同,它会更积极地寻找一些控件来获得焦点。

请注意,当您按Alt+Tab键切换到另一个窗口时,会得到完全相同的行为。再次按Alt+Tab键可切换回。

解释这是一个相当长的故事,我会尽量精简它。焦点在GUI中是一件大事,按键被发送到当前具有焦点的窗口。在拥有它的线程中只能有一个窗口,或者没有。这样的窗口应该是为处理焦点而设计的窗口。它应该指示它,通常用一个焦点矩形或插入符号,有时是一种不同的颜色。当然,用关键的笔画做一些有意义的事情。你肯定知道它们,按钮,文本框,列表框等等。

Form类不是其中之一。它是容器控件,是可以接收焦点的控件的主页。它非常、非常不愿意接受焦点。由于其控制样式的加强,它关闭了ControlStyles.Selectable,打开了ControlStyls.ContainerControl。容器控件的其他示例有Panel、UserControl、ToolStrip、GroupBox。

因此,当您将按钮的Visible属性设置为false时,Winforms需要找到另一个控件来将焦点提供给它。没有剩下的控件,它放弃了,也没有焦点窗口了。当您重新激活窗口时,VB6兼容性开始发挥作用,窗体确实获得焦点。

容器控件对按键感兴趣的原因只有一个,它应该使用它们来实现快捷键。像Alt+F4关闭窗口,F1显示帮助,Alt+F激活文件菜单,等等。无论哪个控件具有焦点,都应该起作用的按键。注意,您得到的常见建议是使用表单的KeyPreview属性,但这是另一个麻烦的VB6兼容性属性。它不适用于所有的击键,并且是特定于窗体的。

正确的方法是重写ProcessCmdKey()方法。

问题是,当您单击按钮时,它会获得并保持焦点,即使在您将其隐藏在代码中之后也是如此。离开以放置断点并返回到应用程序是欺骗性的,因为关键步骤实际上只是将焦点返回到表单(以便触发其KeyDown处理程序)。您只需切换到任何其他应用程序,然后返回到您的表单即可完成此操作

将KeyPreview设置为true意味着窗体可以"窥视"指定给其他窗口控件(如按钮)的事件。当按钮具有焦点时,表单不会看到keydown事件,而是指向按钮。

转向更现代的WPF框架的一个重要原因是,当您有一个深刻而复杂的组件层次结构时,它可以更好地处理这样的事件。集中处理程序变得容易多了。

这项工作可能会帮助您

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
        //your code
        return base.ProcessCmdKey(ref msg, keyData);
    }