首页 > 解决方案 > WinForms TopLevel false 表单与 TextBox 鼠标选择混淆

问题描述

我从一个 MDI 应用程序开始,但窗口管理对用户不友好,所以我将所有内容都转换为选项卡式浏览模型。

当我使用 TopLevel = false 设置一个表单并将其设置为另一个表单的子级时,一切正常,除了当您单击 TextBox 中的文本时,光标会转到 TextBox 的开头而不是您单击的位置。您不能单击特定的插入点,也不能选择文本范围。键盘输入不受影响。这里有一些简单的代码来重现这个:

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        var form1 = new Form();
        var form2 = new Form();
        var tab = new TabControl();
        tab.TabPages.Add("first");
        tab.Dock = DockStyle.Fill;
        form2.TopLevel = false;
        form1.Controls.Add(tab);
        tab.TabPages[0].Controls.Add(form2);
        form2.Controls.Add(new TextBox { Top = 10, Left = 10, Text = "Blah blah blah" });
        form2.Controls.Add(new TextBox { Top = 35, Left = 10, Text = "more text here blah blah" });
        form2.Visible = true;
        Application.Run(form1);
    }
}

只需运行此示例代码,然后单击任一文本框中的文本中间即可。

我覆盖了 WndProc,当单击不同的框(> 箭头数表示嵌套调用)时,看起来表单按此顺序收到以下消息:

> form2.WM_PARENTNOTIFY
> form1.WM_PARENTNOTIFY
> form2.WM_MOUSEACTIVATE
>> form1.WM_MOUSEACTIVATE
> form1.WM_WINDOWPOSCHANGING
> form1.WM_WINDOWPOSITIONCHANGED
> form1.WM_ACTIVATEAPP
> form1.WM_NCACTIVATE
> form1.WM_ACTIVATE

我还尝试了什么:我做了很多工作,将子表单转换为带有标题栏、关闭按钮、拖动调整大小和移动等的用户控件,但是它出现了各种各样的问题,事件没有正确连接,因为没有FormClosing/FormClosed 事件、自定义控件查找错误的父表单等。

因此,使子窗体 TopLevel = false 而不是 MdiChild 正是我需要的,除了这个错误。所有其他控件都正确获得焦点,并按预期与键盘和鼠标交互。这是 WinForms TextBox 控件的一个特定问题。

有谁知道如何解决上述示例应用程序显示的 TextBox 焦点问题?

标签: c#winforms

解决方案


我事先做了一大堆搜索,但直到发布后的第二天,我才发现Windows Forms: Unable to Click to Focus a MaskedTextBox in a Non TopLevel Form via the related questions feature。事实证明,这个问题主要是那个问题的重复。Hans Passant 对这个问题的回答给了我正确的方向。

将所有子窗体转换为 UserControls 不起作用,因为期望由窗体托管的东西正在寻找顶级窗体而不是子窗体。但汉斯的回答指出,如果在表单上设置 TopLevel = false 和 FormBorderStyle = None,则 Form 基本上会像普通控件一样工作。通过这种方式,所有需要 Form 的东西实际上都会得到一个 Form。

因此,我为 UserControls 编写的所有窗口管理代码都证明是有用的(绘制和处理可拖动边框、标题栏、最大化和关闭按钮),我所要做的就是将 UserControls 转换回 TopLevel = false 的窗体,并且然后在 OnShown 中设置 FormBorderStyle = None。


推荐阅读