通过单击 Anywhere/Anything Else 从 TextBox 中移除焦点

Remove Focus from TextBox by Clicking Anywhere/Anything Else

有没有一种直接的方法可以在单击表单中的任何其他地方时使 TextBox 失去焦点,无论被单击的东西本身是否可以获得焦点(例如工具栏按钮、工具栏 and/or客户区为空 space,等等)?也就是说,散焦点击事件可能发生在其他各种对聚焦控件一无所知的控件上。

这个问题与 this one and this one 非常相似,但那里的答案并不完整,因为它们没有提供任何关于何时执行操作的建议(即什么事件在什么控件上)。

我有一个表单(UserControl subclass),面板上有可拖动的小部件以及几个工具栏。可拖动小部件由 PictureBoxLabel 组成。双击小部件的标签会将标签替换为 TextBox 以允许用户重命名组件。目的是在单击 其他任何地方 时强制焦点离开 TextBox,以进行验证(就像用户按下 Enter 键一样)。

以下是层次结构的简要概述:

用户控件
  +- ToolStripContainer
     +- ToolStripConainer.TopToolStripPanel
     | +- 工具条
     +- ToolStripContainer.RightToolStripPanel
     | +- 工具条
     +- ToolStripContainer.ContentPanel
        +- CompositorPanel(面板的 subclass 以提供适当的透明度)
           +- 用户控件(可拖动小部件)
              +- 图片框
              +- 标签
              +- 文本框

我可以向表单上的各种其他组件添加事件处理程序以强制聚焦的 TextBox 取消聚焦(即以编程方式将焦点移动到另一个元素),但这看起来很笨拙并且需要不相关的元素才能意识到当前聚焦的文本框。

我尝试在最外面的 class 上重写 OnMouseDown(包含 UserControl 的所有内容,基于提出的想法 here),但从未调用该方法。

为了检测是否在 TextBox 之外的任何地方单击了鼠标,需要 mouse hook,或者您可以通过在 UserControl 上放置一个计时器来尝试简单的 hack:

public UserControl1() {
  InitializeComponent();
  mouseTimer.Interval = 16;
  mouseTimer.Tick += mouseTimer_Tick;
  mouseTimer.Enabled = true;
}

private void mouseTimer_Tick(object sender, EventArgs e) {
  if (this.ActiveControl != null && this.ActiveControl.Equals(textBox1)) {
    if (MouseButtons != MouseButtons.None) {
      if (!textBox1.ClientRectangle.Contains(textBox1.PointToClient(MousePosition))) {
        if (this.ParentForm != null) {
          this.ParentForm.ActiveControl = null;
        } else {
          this.ActiveControl = null;
        }
      }
    }
  }
}