【问题标题】:Make a form not focusable in C#在 C# 中使表单不可聚焦
【发布时间】:2023-03-26 16:40:01
【问题描述】:

我想编写一个虚拟键盘,例如用于触摸屏电脑的 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");
}

有没有办法做到这一点?有什么更好的方式让表单向正在使用的应用程序发送键盘事件的建议吗?

谢谢!!

【问题讨论】:

    标签: c# forms keyboard focus sendkeys


    【解决方案1】:

    解决了!

    我已经尝试过 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;
        }
    }
    

    【讨论】:

    • 您不应该从技术上添加createParams.ExStyle,而不是完全替换它吗? (例如,createParams.ExStyle |= WS_EX_NOA[...];
    • 我编辑了答案中的代码以实现该更改
    • @DavidHeffernan 好的,所以您对其进行了编辑以实现 Jesse 建议的更改?
    • @barlop 是的。您可以在现在的答案中清楚地看到这一点,您可以通过单击文本“edited Apr ....”来查看编辑历史记录。
    • @DavidHeffernan 是的,我还不够先进,无法了解 jesse 对 .ExStyle 的含义。我看到添加了管道符号的编辑我不确定这就是 jesse 的全部含义。感谢您的确认。
    【解决方案2】:

    与其在你的窗口被点击后尝试重置活动窗口,我宁愿尝试阻止你的窗口接收焦点/被激活。

    看看this article。最后,作者简要说明了如何做到这一点:

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

    在 Windows Forms 2.0 中,有一个名为的新属性 ShowWithoutActivation - 您需要在表单上覆盖它。 在本机应用程序中,您可以将 SetWindowPos 与 SWP_NOACTIVATE 标志或带有 SW_SHOWNA 标志的 ShowWindow。

    此外,他在this article 中提供了一个 Windows 窗体的代码示例:

    如果您想要一个完整的表单,您现在可以覆盖一个名为 ShowWithoutActivation:

    公共类 NoActivateForm : 表单
    {
        受保护的覆盖 bool ShowWithoutActivation => true;
    }

    请记住,这不会一直“阻止”激活 - 您可以 仍然通过调用Activate()Focus()…等方法激活。如果 您想防止点击客户区激活 窗口,您可以处理WM_MOUSEACTIVATE 消息。

    private const int WM_MOUSEACTIVATE = 0x0021, MA_NOACTIVATE = 0x0003;
    
    受保护的覆盖无效 WndProc(参考消息 m)
    {
        如果(m.Msg == WM_MOUSEACTIVATE)
        {
             m.Result = (IntPtr)MA_NOACTIVATE;
             返回;
        }
        base.WndProc(参考 m);
    }

    【讨论】:

    • 谢谢!它已经完成了一半!该文章解决方案阻止我的表单获得焦点,但不会阻止其他应用程序失去焦点。也就是说,当我点击我的应用程序时,我会遇到没有应用程序具有焦点的情况。
    • 万一链接失效,你的回答基本上没有任何信息。请总结场外资源,这样即使场外资源死了,你的回答也能继续有价值。
    猜你喜欢
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 2011-05-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    相关资源
    最近更新 更多