My question: how I can keep my Form always on top (I can do it setting TopMost = true
) without stealing the focus from the Window currently active when my Form is interacted with.
Take a look at the images to better understand my question: the Form is on top and when I focus on any other Input control outside my Form, I can send the selected Emoji to it.
1 : An Image what I want to happen in an input box in FireFox when I click an Emoji Button in my Form.
2 : An Image of my Form: as soon as I click a Button in my Form, the focus moves back to that Form.
FormBorderStyle = none
. Make it TopMost (this setting is another topic per se, I'm no going to discuss it here).WS_EX_NOACTIVATE
and WS_EX_TOOLWINDOW
extended styles. This prevents the Form from being activated when mouse events are generated inside its surface (see the docs about these styles).ButtonNoSel
Control shown below) and set their Tag
property to the Unicode char corresponding to the Emoji image these buttons show.These Buttons, when clicked, simply use SendKeys::Send() (a.k.a. SendInput()
) to send the selected Unicode char to the foreground Window, casting to string the Tag
property value.
This is how it works (setting the Emoji to a Web Page shown by 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());
}
};
Add this simple Custom Control to the Project.
This Control simply uses Control.SetStyle, setting ControlStyles::Selectable = false
to prevent the child Control to become the ActiveControl when interacted with, since it doesn't receive the focus.
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:
~ButtonNoSel() { }
};
}
Note that this Form must run on its own thread.
If you need to show this Form from another one, use Task.Run() to show it as a Dialog Window, calling ShowDialog()
. This will start the Message Loop.
For example, from a Button.Click
handler of another Form (here, named 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));
}