当控件的顶级父窗口是活动窗口时,如何设置焦点?

3
我正在进行一个C#项目。问题是,像鼠标进入这样的背景事件会使一个后台窗口弹出并获得焦点,即使它不是活动窗口。解决策略是在设置此焦点之前添加预防步骤。
我的尝试包括以下策略:
在调用control.Focus()之前,我想实现一个条件,涉及GetActiveWindow()函数以匹配控件的顶级父级关联窗口句柄。对于后者,我使用Control.TopLevelControl()。但是,每次我这样做时,我都会得到null的属性。根据MSDN参考的原因是该控件未父化在一个表单上。
尝试的片段:
if (myControl.TopLevelControl.Handle == GetActiveWindow())
{
     this.myControl.Focus();
}

代码背景:此代码不归我所有,所以请原谅我有些抽象。我会尽可能详细地解释。涉及的控件是一个私有成员自定义布局面板,它继承自System.Windows.Forms.Panel,其中DoubleBuffered属性设置为true。将此控件添加到一个内部部分类中,该分类继承自UserControl(Windows窗体)。后者的控件是通过一个私有成员SplitContainer(Windows窗体)添加到一个公共部分类(同样继承自User Control)中。

在此布局面板的用户控件构造函数中,添加了以下事件:

myLayoutPanel.MouseEnter += this.myLayoutPanel_MouseEnter;

没有我的更改,事件看起来像:

private void myLayoutPanel_MouseEnter(object sender, EventArgs e)
    {
        myLayoutPanel.SuspendLayout();
        myLayoutPanel.Focus();
        myLayoutPanel.ResumeLayout(false);
    }

此外,我手动观察了控件父层次结构的句柄,但从未能够匹配到活动窗口句柄。直觉上,我感觉GetActiveWindow()使用互操作性来深入非托管代码以获取句柄,而顶级属性留在托管区域,因此具有其限制。虽然我可能是错的。
有人对此有什么想法吗?

1
像鼠标进入这样的事件会使一个背景窗口在不是活动窗口时弹出焦点。这实际上并不是正常行为。您是否有在MouseEnter事件中运行的代码,可以将窗体带入焦点? - LarsTech
1
当您不告诉我们为什么您的用户控件未嵌入表单时,您并没有真正帮助我们帮助您。猜测:当myControl.IsHandleCreated为false时不要执行任何操作。 - Hans Passant
你确定你的窗口(表单)是真正活动的吗?即使窗口/表单不是最顶层,它也可能在顶部并处于非活动状态。 - TcKs
1
在winforms(以及Win32 UI中),一切都是动态添加的,这不是问题。问题是,你的表单没有焦点。问题肯定出在别处。我认为可能是在“鼠标进入”区域有问题。尝试更详细地描述这部分内容,我们可能可以提供帮助。 - TcKs
1
就像我之前所说的一样 - 控件的组合并不重要。创建一个最小的代码示例,该示例可以(不)工作。我的提示(在不知道完整代码的情况下)是处理鼠标事件并弹出表单的部分,而不是表单中控件的组合。 - TcKs
显示剩余9条评论
1个回答

3

有两个“焦点”级别。一个是表单内控件的“焦点”。另一个是整个桌面窗口组合中表单的“焦点”。

控件使用这些成员:

这些成员在Form中使用:

如果您通过Focus()将焦点设置到表单内的控件上,则焦点仅限于该表单,并且不会更改表单状态。如果您还想将焦点设置到表单上,您需要激活表单(通过激活())。

要重现此行为的最小代码如下:

public class MyControl : FlowLayoutPanel {
    private TextBox textBox1;
    private TextBox textBox2;

    public MyControl() {
        this.textBox1 = new TextBox();
        this.Controls.Add(this.textBox1);

        this.textBox2 = new TextBox();
        this.Controls.Add(this.textBox2);

        this.BackColor = Color.Blue; // not required

        this.MouseEnter += this.MyControl_MouseEnter;
    }

    private void MyControl_MouseEnter(object sender, EventArgs e) {
        this.textBox1?.Focus(); // sets focus to the control

        var parentForm = this.FindForm();
        parentForm?.Activate(); // activates the form
    }
}

感谢您的回复。但是,在我的应用程序中,FindForm() 返回 null。我相信在根层次结构的核心处,它正在进行 COM 级别的 C++ 调用,而不是基于表单。我只是在检查这个问题。很难想象没有基本表单的用户控件。 - Gaurav Sinha
你的条件很特殊。我真的想帮助你。但是我需要更多的线索。你能提供一些类似于你的条件的代码吗? - TcKs
确切地说,我也很好奇你的问题,而且我一直在从一开始就阅读@TcKs的评论。你不必分享你不拥有的确切代码。只需排除与问题无关的部分,直到你有最小的代码可以重现问题,然后你可以分享它或创建一个类似的示例_确保它也能重现该行为_。否则,很难猜测你的问题的原因。 - 41686d6564 stands w. Palestine
@TcKs,我找到了我的问题。但是没有解决方案。我的顶级控件托管了这个布局面板,并且具有窗口句柄。而且这个控件在基础应用程序框架上。我的顶级控件使用COM接口来获取创建的句柄。我无法从C#的任何属性中获取COM级别的窗口句柄。对于造成的困惑,我很抱歉。我以前没有做过这个。关于NDA有非常严格的政策。所以,我无法将其转化为流畅的内容。但是,我感谢你们给予的帮助。 - Gaurav Sinha
@TcKs和大家:感谢你们的帮助。问题已经通过使用API调用WindowFromPoint而不是使用控件来获取父窗口得到了解决。 - Gaurav Sinha

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