在C#中使表单无法获得焦点

14
我想编写一个虚拟键盘,就像 Windows 屏幕键盘一样适用于触摸屏电脑。但是我遇到了一个问题:我的虚拟键盘会从正在使用的应用程序中夺取焦点。Windows 屏幕键盘即使用户单击它也会保持当前应用程序的焦点。在 C# 中的 Windows 窗体中是否有方法可以实现同样的功能?
目前我唯一能做的事情就是向特定的应用程序发送键盘事件,例如在下面的代码中的记事本。如果我可以让表单不可聚焦,我就可以使用 GetForegroundWindow 获取当前具有焦点的窗口。
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);


[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

private void button1_Click(object sender, EventArgs e)
{
    IntPtr calculatorHandle = FindWindow("notepad", null);
    SetForegroundWindow(calculatorHandle);
    SendKeys.SendWait("111");
}

有没有一种方法可以做到这一点?是否有更好的建议,以便表单可以将键盘事件发送到正在使用的应用程序?

谢谢!

2个回答

16

问题已解决!

我尝试了gehho的解决方案,但还需要重写CreateParams方法:

private const int WS_EX_NOACTIVATE = 0x08000000;
protected override CreateParams CreateParams
{
    get
    {
        var createParams = base.CreateParams;

        createParams.ExStyle |= WS_EX_NOACTIVATE;
        return createParams;
    }
}

3
从技术角度来讲,您应该对 createParams.ExStyle 进行添加而不是完全替换它吧?比如说,使用 createParams.ExStyle |= WS_EX_NOA[...] - Jesse
我编辑了答案中的代码以实现那个更改。 - David Heffernan
@barlop 是的。您可以清楚地在当前答案中看到,也可以通过单击文本“编辑于Apr ....”来查看编辑历史记录。 - David Heffernan
@DavidHeffernan,是的,我不够高级,不知道Jesse所说的.ExStyle是什么意思。我看到了添加了一个管道符号的编辑,但我不确定那是否就是Jesse所说的全部。感谢您的确认。 - barlop
我发现这段代码可以独立运行,不需要使用gehho提到的代码。你有没有发现添加gehho提到的代码有什么优势?我发现gehho的代码对我不起作用,但是这个代码可以。不是作为它的补充,而是单独使用。 - barlop
显示剩余2条评论

9

与其在你的窗口被点击后尝试重置活动窗口,我更愿意尝试防止你的窗口获得焦点/被激活。

看一下这篇文章。作者在结尾简要解释了如何做到这一点:

如何防止我的窗口在显示时获得激活和焦点?

在Windows Forms 2.0中有一个新属性叫做ShowWithoutActivation——你需要在Form上进行重写。 在本机应用程序中,您可以使用带有SWP_NOACTIVATE标志的SetWindowPos或带有SW_SHOWNA标志的ShowWindow。

此外,在这篇文章中,他提供了一个Windows Forms的代码示例:

如果您想要一个全功能的表单,现在您可以重写一个名为 ShowWithoutActivation的属性:

public class NoActivateForm : Form 
{
    protected override bool ShowWithoutActivation => true;  
}

请记住,这并不是始终“防止”激活——您仍然可以通过调用Activate()Focus()等方法来激活。 如果您想要防止单击客户区域激活窗口,您可以处理WM_MOUSEACTIVATE消息。

private const int WM_MOUSEACTIVATE = 0x0021, MA_NOACTIVATE = 0x0003;

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_MOUSEACTIVATE) 
    {
         m.Result = (IntPtr)MA_NOACTIVATE;
         return;
    }
    base.WndProc(ref m);
}

谢谢!已经完成了一半!那篇文章的解决方案可以防止我的表单获得焦点,但不能防止其他应用程序失去焦点。也就是说,当我点击我的应用程序时,我会遇到没有应用程序拥有焦点的情况。 - Jandex
如果链接失效,你的回答基本上就没有任何信息。请总结一下离线资源,这样即使离线资源消失,你的回答仍然有价值。 - IInspectable

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