Notification Window - Prevent Window Focus

I'm having trouble getting a notification window in order to behave correctly in C #. Basically I show a limitless form in the lower right of the screen that displays a message for a few seconds and then disappears. The problem is that I need it to appear on top of other windows, not being able to steal focus. Ideally, I want it to be purely managed code, although looking through similar examples, I doubt it will be possible.

At the moment, I'm stopping him from stealing focus when calling Form.Show () with an override:

protected override bool ShowWithoutActivation // stops the window from stealing focus { get { return true; } } 

and then ignoring mouse clicks with:

  private const int WM_MOUSEACTIVATE = 0x0021; private const int MA_NOACTIVATEANDEAT = 0x0004; protected override void WndProc(ref Message m) { if (m.Msg == WM_MOUSEACTIVATE) { m.Result = (IntPtr)MA_NOACTIVATEANDEAT; return; } base.WndProc(ref m); } 

However, I find that if I use them together with TopMost = true (what I need), it still gets focus, and if all other windows are minimized, it also gets focus.

So, is there any smoothing method so that the form never gains focus (be it with a mouse click, alt-tab, etc.), although it is still the top of the largest / second top? Even just by focusing on the window that he stole, it will work (although it introduces a flicker).

Any suggestions would be greatly appreciated, I am really stuck with this.

EDIT:

Ok, so I finally managed to get this working using:

 protected override bool ShowWithoutActivation // stops the window from stealing focus { get { return true; } } // and const int WS_EX_NOACTIVATE = 0x08000000; const int WS_EX_TOPMOST = 0x00000008; protected override CreateParams CreateParams { get { CreateParams param = base.CreateParams; param.ExStyle |= WS_EX_TOPMOST; // make the form topmost param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated return param; } } // and [DllImport("user32.dll")] private extern static IntPtr SetActiveWindow(IntPtr handle); private const int WM_ACTIVATE = 6; private const int WA_INACTIVE = 0; private const int WM_MOUSEACTIVATE = 0x0021; private const int MA_NOACTIVATEANDEAT = 0x0004; protected override void WndProc(ref Message m) { if (m.Msg == WM_MOUSEACTIVATE) { m.Result = (IntPtr)MA_NOACTIVATEANDEAT; // prevent the form from being clicked and gaining focus return; } if (m.Msg == WM_ACTIVATE) // if a message gets through to activate the form somehow { if (((int)m.WParam & 0xFFFF) != WA_INACTIVE) { if (m.LParam != IntPtr.Zero) { SetActiveWindow(m.LParam); } else { // Could not find sender, just in-activate it. SetActiveWindow(IntPtr.Zero); } } } 

I also added Form.Hide () to the GotFocus event so that even if it was somehow focused, it just closes and leaves the user path as soon as possible.

Also, if someone is wondering, constants for all window styles, etc. can be found at WINUSER.H, its online at http://www.woodmann.com/fravia/sources/WINUSER.H if you cannot find it.

However, if someone can see a more elegant way to do this, it will be appreciated.

+6
source share
3 answers

In WPF, try the following:

 ShowActivated="False" 
+3
source

Perhaps the WS_EX_NOACTIVATE extended window style is what you are looking for. A window with this style is not activated when clicked. For example, a virtual keyboard window has this style.

To apply this style to a window, override the CreateParams function and change the baseParams.ExStyle.

+2
source

I'm not looking for points here, as the original poster has already published a solution that worked for them, but I wanted to share my experience with this problem. Using the solution above (which is at the bottom of the question, not in the answer form) gives me a Win32Exception: Error creating window handle error. when using the WndProc code as it is sent there. The ShowWithoutActivation and ShowWithoutActivation work fine to prevent the form from activating and still keep it at the top.

I came up with an alternative solution to prevent the form from being clicked using SetWindowLong , which makes the form transparent and therefore can be clicked, and SetLayeredWindowAttributes , which returns transparency so you can see the form again, but still retain the ability to click on the form.

NOTE. . You -CANNOT- interact with the form in general in this state and even try to press the "X" button, just click everything that is behind the form in this position, so you need to use the code to close the form:

 public partial class Form1 : Form { private enum GWL : int { ExStyle = -20 } private enum WS_EX : int { Transparent = 0x20, Layered = 0x80000 } public enum LWA : int { ColorKey = 0x1, Alpha = 0x2 } [DllImport("user32.dll")] static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll")] static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); protected override bool ShowWithoutActivation { get { return true; } } const int WS_EX_NOACTIVATE = 0x08000000; const int WS_EX_TOPMOST = 0x00000008; protected override CreateParams CreateParams { get { CreateParams param = base.CreateParams; param.ExStyle |= WS_EX_TOPMOST; // make the form topmost param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated return param; } } public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { // Prevent form from showing up in taskbar which also prevents activation by Alt+Tab this.ShowInTaskbar = false; // Allow the form to be clicked through so that the message never physically interferes with work being done on the computer SetWindowLong(this.Handle, (int)GWL.ExStyle, (int)WS_EX.Layered | (int)WS_EX.Transparent); // Set the opacity of the form byte nOpacity = 255; // 0 = invisible, 255 = solid, anything inbetween will show the form with transparency SetLayeredWindowAttributes(this.Handle, 0, nOpacity, (uint)LWA.Alpha); } } 

I also managed to get the original approach to work by making a small change to the WndProc code above.

NOTE. . It also makes the form unattractive, but the behavior is different in that you can press the min, max, and 'X' buttons, but nothing happens when you do. The mouse cursor also changes when you are on the edge of the form, as if to resize, but does not allow resizing:

 public partial class Form1 : Form { protected override bool ShowWithoutActivation { get { return true; } } const int WS_EX_NOACTIVATE = 0x08000000; const int WS_EX_TOPMOST = 0x00000008; protected override CreateParams CreateParams { get { CreateParams param = base.CreateParams; param.ExStyle |= WS_EX_TOPMOST; // make the form topmost param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated return param; } } [DllImport("user32.dll")] private extern static IntPtr SetActiveWindow(IntPtr handle); private const int WM_ACTIVATE = 6; private const int WA_INACTIVE = 0; private const int WM_MOUSEACTIVATE = 0x0021; private const int MA_NOACTIVATEANDEAT = 0x0004; protected override void WndProc(ref Message m) { if (m.Msg == WM_MOUSEACTIVATE) { m.Result = (IntPtr)MA_NOACTIVATEANDEAT; // prevent the form from being clicked and gaining focus return; } if (m.Msg == WM_ACTIVATE) // if a message gets through to activate the form somehow { if (((int)m.WParam & 0xFFFF) != WA_INACTIVE) { if (m.LParam != IntPtr.Zero) { SetActiveWindow(m.LParam); } else { // Could not find sender, just in-activate it. SetActiveWindow(IntPtr.Zero); } } } else { base.WndProc(ref m); } } public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { // Prevent form from showing up in taskbar which also prevents activation by Alt+Tab this.ShowInTaskbar = false; } } 
0
source

Source: https://habr.com/ru/post/899195/


All Articles