如何在不从活动 Window 窃取焦点的情况下使表单始终保持在顶部?
How to keep a Form always on top without stealing focus from the active Window?
我的问题:当与我的表单交互时,我如何才能让我的表单始终位于顶部(我可以通过设置 TopMost = true
来做到这一点)而不窃取当前活动的 Window 的焦点。
查看图片以更好地理解我的问题:表单位于顶部,当我关注表单之外的任何其他输入控件时,我可以将选定的表情符号发送给它。
1 : 当我单击表单中的表情符号按钮时,我希望在 FireFox 的输入框中出现的图像。
2:我的表单的图像:只要我单击表单中的按钮,焦点就会移回该表单。
- 做一个标准的Form,设置它
FormBorderStyle = none
。 Make it TopMost(这个设置本身是另一个话题,我不打算在这里讨论)。
- 覆盖 CreateParams to set the
WS_EX_NOACTIVATE
and WS_EX_TOOLWINDOW
扩展样式。这可以防止在其表面内生成鼠标事件时激活表单(请参阅有关这些样式的文档)。
- 向其添加一些不可选择的按钮控件(如下所示的
ButtonNoSel
控件)并将它们的 Tag
属性 设置为与这些按钮显示的表情符号图像对应的 Unicode 字符。
- 向所有按钮添加相同的点击处理程序。
这些按钮,单击时,只需使用 SendKeys::Send() (a.k.a. SendInput()
) 将选定的 Unicode 字符发送到前台 Window,转换为字符串Tag
属性 值。
这是它的工作原理(将表情符号设置为 FireFox 显示的网页):
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);
}
private:
void InitializeComponent(void) {
this->UseVisualStyleBackColor = true;
}
protected:
// delete managed resources, if any
~ButtonNoSel() { this->!ButtonNoSel(); }
// delete unmanaged resources, if any
!ButtonNoSel() { }
};
}
请注意,此表单必须 运行 在其自己的线程中。
如果您需要从另一个表单显示此表单,请使用 Task.Run() 将其显示为对话框 Window,调用 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));
}
我的问题:当与我的表单交互时,我如何才能让我的表单始终位于顶部(我可以通过设置 TopMost = true
来做到这一点)而不窃取当前活动的 Window 的焦点。
查看图片以更好地理解我的问题:表单位于顶部,当我关注表单之外的任何其他输入控件时,我可以将选定的表情符号发送给它。
1 : 当我单击表单中的表情符号按钮时,我希望在 FireFox 的输入框中出现的图像。
2:我的表单的图像:只要我单击表单中的按钮,焦点就会移回该表单。
- 做一个标准的Form,设置它
FormBorderStyle = none
。 Make it TopMost(这个设置本身是另一个话题,我不打算在这里讨论)。 - 覆盖 CreateParams to set the
WS_EX_NOACTIVATE
andWS_EX_TOOLWINDOW
扩展样式。这可以防止在其表面内生成鼠标事件时激活表单(请参阅有关这些样式的文档)。 - 向其添加一些不可选择的按钮控件(如下所示的
ButtonNoSel
控件)并将它们的Tag
属性 设置为与这些按钮显示的表情符号图像对应的 Unicode 字符。 - 向所有按钮添加相同的点击处理程序。
这些按钮,单击时,只需使用 SendKeys::Send() (a.k.a. SendInput()
) 将选定的 Unicode 字符发送到前台 Window,转换为字符串Tag
属性 值。
这是它的工作原理(将表情符号设置为 FireFox 显示的网页):
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);
}
private:
void InitializeComponent(void) {
this->UseVisualStyleBackColor = true;
}
protected:
// delete managed resources, if any
~ButtonNoSel() { this->!ButtonNoSel(); }
// delete unmanaged resources, if any
!ButtonNoSel() { }
};
}
请注意,此表单必须 运行 在其自己的线程中。
如果您需要从另一个表单显示此表单,请使用 Task.Run() 将其显示为对话框 Window,调用 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));
}