如何在不从活动窗口中窃取焦点的情况下保持表单始终在顶部?

问题描述

我的问题:如何让我的表单始终保持在最前面(我可以设置 TopMost = true),而不会在与我的表单交互时从当前活动的窗口中窃取焦点。

查看图像以更好地理解我的问题:表单位于顶部,当我关注表单之外的任何其他输入控件时,我可以将选定的表情符号发送给它。

1:当我单击表单中的表情符号按钮时,我希望在 FireFox 的输入框中发生的图像。

enter image description here

2:我的表单的图像:只要我在我的表单中单击一个按钮,焦点就会移回该表单。

enter image description here

解决方法

  1. 制作标准表单,设置其FormBorderStyle = none。设为 TopMost(此设置本身是另一个主题,我不打算在这里讨论它)。
  2. 覆盖 CreateParams 以设置 WS_EX_NOACTIVATE and WS_EX_TOOLWINDOW 扩展样式。这可以防止在窗体表面内生成鼠标事件时激活窗体(请参阅有关这些样式的文档)。
  3. 向其中添加一些不可选择的按钮控件(如下所示的 ButtonNoSel 控件)并将它们的 Tag 属性设置为与这些按钮的表情符号图像对应的 Unicode 字符显示。
  4. 向所有按钮添加相同的 Click 处理程序。

点击这些按钮时,只需使用 SendKeys::Send()(又名 SendInput())将选定的 Unicode 字符发送到前台窗口,将 Tag 属性值转换为字符串。

这是它的工作原理(将表情符号设置为 FireFox 显示的网页):

enter image description here


using namespace System;
using namespace System::ComponentModel;
using namespace System::Windows::Forms;
using namespace System::Drawing;

public ref class frmKeyBoard : public System::Windows::Forms::Form
{
public:
    frmKeyBoard(void) { InitializeComponent(); }

// [...] Initialization...

protected:
    property System::Windows::Forms::CreateParams^ CreateParams {
        virtual System::Windows::Forms::CreateParams^ get() override {
            auto cp = Form::CreateParams;
            cp->ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW);
            return cp;
        }
    }

// Event handler for all the Buttons
private:
    System::Void buttonNoSelAll_Click(System::Object^  sender,System::EventArgs^  e) {
        Control^ ctl = safe_cast<Control^>(sender);
        SendKeys::Send(ctl->Tag->ToString());
    }
};

将这个简单的自定义控件添加到项目中。
此控件仅使用 Control.SetStyle,设置 ControlStyles::Selectable = false 以防止子控件在与之交互时成为 ActiveControl,因为它没有获得焦点。

using namespace System;
using namespace System::Windows::Forms;
using namespace System::ComponentModel;

namespace CustomControls {
    [ToolboxItem(true)]
    public ref class ButtonNoSel : public System::Windows::Forms::Button
    {
    public:
        ButtonNoSel(void) {
            this->InitializeComponent();
            this->SetStyle(ControlStyles::Selectable,false);
        }
    protected:
        ~ButtonNoSel() { }
        !ButtonNoSel() { }
        void InitializeComponent(void) {
            this->UseVisualStyleBackColor = true;
        }
    };
}

注意这个Form必须在自己的线程上运行。
如果您需要从另一个表单显示此表单,请使用 Task.Run() 将其显示为对话框窗口,调用 ShowDialog()。这将启动消息循环。

例如,来自另一个表单的 Button.Click 处理程序(此处名为 MainForm)。

private: 
    void ShowKeyboard() {
        frmKeyBoard^ fk = gcnew frmKeyBoard();
        fk->ShowDialog();
        delete fk;
    }

private: 
    System::Void button1_Click(System::Object^  sender,System::EventArgs^  e) {
        Task::Run(gcnew Action(this,&MainForm::ShowKeyboard));
    }

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...