通知窗口 – 防止窗口获得焦点

我在收到通知框以在c#中正常运行时遇到一些问题。 基本上我在屏幕的右下方显示一个无边框forms,它显示一条消息几秒钟然后消失。 问题是我需要它出现在其他窗口的顶部,而不能偷取焦点。 理想情况下,我希望它是纯粹的托管代码,虽然通过类似的例子我怀疑这是可能的。

目前我在使用覆盖调用Form.Show()时阻止它窃取焦点:

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

然后忽略鼠标点击:

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

但是我发现,如果我将它们与TopMost = true(我需要)一起使用,它无论如何都会获得焦点,如果所有其他窗口都被最小化,它也会获得焦点。

那么,是否有任何方法可以防止表格获得焦点(无论是通过鼠标点击,alt-tab等),同时仍然是最顶级/最顶级的forms? 即使只是将焦点立即重新放回窗口,它也会将其偷走(尽管会引入闪烁)。

任何建议都会非常感激,我真的很喜欢这个。

编辑:

好的,所以我终于设法让这个工作使用:

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

我还将Form.Hide()添加到GotFocus事件中,这样即使它以某种方式获得焦点,它也会关闭并尽快退出用户。

此外,如果有人想知道,所有窗口样式的常量等都可以在WINUSER.H中找到,如果你找不到,可以在http://www.woodmann.com/fravia/sources/WINUSER.H上找到它。它。

但是,如果有人能够看到更优雅的方式,我们将不胜感激。

在WPF中试试这个:

 ShowActivated="False" 

可能WS_EX_NOACTIVATE扩展窗口样式是您正在寻找的。 单击时不激活具有此样式的窗口。 例如,“虚拟键盘”窗口具有此样式。

要将此样式应用于窗口,请覆盖CreateParams函数并更改baseParams.ExStyle。

我不是在寻找积分,因为原始海报已经发布了一个适合他们的解决方案,但我想分享我对这个问题的经验。 使用上面的解决方案(位于问题的底部而不是答案forms)给我一个Win32Exception: Error creating window handle error. 当使用WndProc代码时,它会在那里发布。 ShowWithoutActivationCreateParams部分可以很好地防止激活表单并仍然保持最顶层。

我想出了一个替代解决方案,防止使用SetWindowLong点击表单,这使得表单透明,因此可以点击它和SetLayeredWindowAttributes ,它将透明度恢复正常,这样你可以再次看到表单,但仍保留点击表格。

注意:您在此状态下完全与表单交互 – 甚至尝试单击“X”按钮,只需单击该位置表单后面的任何内容,因此您需要使用代码关闭表单:

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

通过对上面的WndProc代码进行一些小改动,我也能够使原始方法起作用。

注意:这也使表单不可点击但行为不同,因为您实际上可以单击最小,最大和“X”按钮,但是当您这样做时没有任何反应。 当您处于窗体边缘时,鼠标光标也会更改,就像resize一样但不允许resize:

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