Topmost Form, Clicking "Through" Possible

Topmost form, clicking through possible?

You can use SetWindowLong to set the WS_EX_TRANSPARENT window style:

If the layered window has the WS_EX_TRANSPARENT extended window style, the shape of the layered window will be ignored and the mouse events will be passed to the other windows underneath the layered window.

CodeProject has this article detailing the technique. Though it's in VB.NET it should be easy to convert to C#.

I have used the following code in the past:

public enum GWL
{
ExStyle = -20
}

public enum WS_EX
{
Transparent = 0x20,
Layered = 0x80000
}

public enum LWA
{
ColorKey = 0x1,
Alpha = 0x2
}

[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
public static extern int GetWindowLong(IntPtr hWnd, GWL nIndex);

[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
public static extern int SetWindowLong(IntPtr hWnd, GWL nIndex, int dwNewLong);

[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
public static extern bool SetLayeredWindowAttributes(IntPtr hWnd, int crKey, byte alpha, LWA dwFlags);

protected override void OnShown(EventArgs e)
{
base.OnShown(e);
int wl = GetWindowLong(this.Handle, GWL.ExStyle);
wl = wl | 0x80000 | 0x20;
SetWindowLong(this.Handle, GWL.ExStyle, wl);
SetLayeredWindowAttributes(this.Handle, 0, 128, LWA.Alpha);
}

but it also was copied from somewhere else. The important lines here are in the OnShown method. Though I have to admit that the line

wl = wl | 0x80000 | 0x20;

is a little cryptic, setting the WS_EX_LAYERED and WS_EX_TRANSPARENT extended styles.

You can probably also set it like

wl = wl | WS_EX.Layered | WS_EX.Transparent;

Is it possible to keep a Form on top of another, but not TopMost?

Owned forms are always displayed on top of their owner form. To make a form owned by an owner, you can assign a reference of the owner form to Onwer property of the owned form, for example:

var f = new Form();
f.Owner = this;
f.Show();

Set a Window of another Process as Owner

To do so, you should first find the handle of window of the other process, then using SetWindowLong API function, you can set it as owner of your form, for example:

//using System.Runtime.InteropServices;
//using System.Diagnostics;

[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
private void button1_Click(object sender, EventArgs e)
{
var notepad = Process.GetProcessesByName("notepad").FirstOrDefault();
if(notepad!=null)
{
var owner = notepad.MainWindowHandle;
var owned = this.Handle;
var i = SetWindowLong(owned, -8 /*GWL_HWNDPARENT*/, owner);
}
}

In above example, your form will be always on top of the notepad window.

this.TopMost = true not working?

TopMost is a property that is used to make sure one window is always shown above all others within an application. Microsofts example was a find and replace tool.

The difference you are finding is that Form1 was created as a modal dialog through the use of ShowDialog. Show dialog makes sure that your form must be closed before all other windows in the application can be used again. For example; using a form to gain user data to enter into a parent forms database.

Show is used when you don't mind if your user has finished with their dialog or not, such as allowing your user the chance to use some utility (e.g timer, stopwatch) that will assist within the main function of a program.

The only visual difference I can think of when using different .Net frameworks, is different windows dialogs such as the OpenFileDialog, that have been updated throughout the framework

Windows Forms: Pass clicks through a partially transparent always-on-top window

You can make a window, click-through by adding WS_EX_LAYERED and WS_EX_TRANSPARENT styles to its extended styles. Also to make it always on top set its TopMost to true and to make it semi-transparent use suitable Opacity value:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Opacity = 0.5;
this.TopMost = true;
}
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
const int GWL_EXSTYLE = -20;
const int WS_EX_LAYERED = 0x80000;
const int WS_EX_TRANSPARENT = 0x20;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
var style = GetWindowLong(this.Handle, GWL_EXSTYLE);
SetWindowLong(this.Handle,GWL_EXSTYLE , style | WS_EX_LAYERED | WS_EX_TRANSPARENT);
}
}

Sample Result

enter image description here

Opening a WinForms Form with TopMost = true but not having it steal focus?

Paste this code in your form:

protected override bool ShowWithoutActivation
{
get { return true; }
}

How to keep a Form always on top without stealing focus from the active Window?

  1. Make a standard Form, set its FormBorderStyle = none. Make it TopMost (this setting is another topic per se, I'm no going to discuss it here).
  2. Override CreateParams to set the 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).
  3. Add some non-selectable Button Controls to it (the ButtonNoSel Control shown below) and set their Tag property to the Unicode char corresponding to the Emoji image these buttons show.
  4. Add the same Click handler to all the Buttons.

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):

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());
}
};

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));
}

How to make a window always stay on top in .Net?

Form.TopMost will work unless the other program is creating topmost windows.

There is no way to create a window that is not covered by new topmost windows of another process. Raymond Chen explained why.



Related Topics



Leave a reply



Submit