通知窗口 - 防止窗口获得焦点
我在让通知框在 C# 中正常运行时遇到一些问题。基本上,我在屏幕的右下角显示一个无边框表单,它会显示一条消息几秒钟,然后消失。问题是我需要它出现在其他窗口的顶部,而不能窃取焦点。理想情况下,我希望它是纯粹的托管代码,尽管查看类似的示例我怀疑这是可能的。
目前,我正在使用 override: 调用 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 等),同时仍然是最上面/第二最上面的表单?即使只是立即将焦点返回到它窃取焦点的窗口也会起作用(尽管会引入闪烁)。
任何建议将不胜感激,我真的很坚持这一点。
编辑:
好的,所以我终于设法使用以下方法使其工作:
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(如果找不到)。
然而,如果有人能看到一种更优雅的方式来做到这一点,我们将不胜感激。
I'm having some issues getting a notification box to behave correctly in c#. Basically I'm showing a boarderless form in the lower right hand side of the screen, which displays a message for a few seconds and then disappears. The problem is that I need it to appear on top of other windows without it ever being able to steal focus. Ideally, I want it to be purely managed code, although looking through similar examples I doubt this will be possible.
At the moment I'm preventing it 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 these in conjunction with TopMost = true (which I need), it gains focus anyway, and if all other windows are minimised, it also gains focus.
So, is there any way to flat out prevent a form from ever gaining focus (whether via mouse click, alt-tab, etc), while still being the top most/second top most form? Even just giving focus immediately back to the window it stole it from would work (although introduce flickering).
Any suggestions would be greatly appreciated, I'm 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 does somehow get focus, it simply closes and gets out of the users way asap.
Also, if anyone is wondering, the constants for all the window styles etc. can be found in WINUSER.H, its online at http://www.woodmann.com/fravia/sources/WINUSER.H if you can't find it.
However, if anyone can see a more elegant way of doing this, it would be appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
可能 WS_EX_NOACTIVATE 扩展窗口样式就是您正在寻找的。单击时不会激活具有此样式的窗口。例如,虚拟键盘窗口就有这种风格。
要将此样式应用于窗口,请重写 CreateParams 函数并更改 baseParams.ExStyle。
Possibly WS_EX_NOACTIVATE extended window style is what you are looking for. Window with this style is not activated when clicked. For example, Virtual Keyboard window has this style.
To apply this style to window, override CreateParams function and change baseParams.ExStyle.
在 WPF 中试试这个:
In WPF try this:
我不是在这里寻找要点,因为原始海报已经发布了适合他们的解决方案,但我想分享我对此问题的经验。使用上面的解决方案(位于问题的底部而不是答案的形式)给我一个
Win32Exception:创建窗口句柄错误错误。
当使用WndProc
代码作为它被张贴在那里。ShowWithoutActivation
和CreateParams
部分非常适合防止激活表单并仍将其保持在最顶层。我想出了一个替代解决方案,使用
SetWindowLong
防止单击表单,这使表单透明,因此可以单击它,并使用SetLayeredWindowAttributes
将透明度设置回正常,这样您就可以再次看到表单,但仍然保留单击表单的能力。注意:在这种状态下,您根本无法与表单交互,甚至尝试单击“X”按钮也只会单击表单后面该位置的任何内容,因此您需要使用代码关闭表单:
我还可以通过对上面的 WndProc 代码进行一些小的更改来使原始方法发挥作用。
注意:这也会使表单不可点击,但行为有所不同,因为您实际上可以点击“最小”、“最大”和“X”按钮,但这样做时什么也不会发生。当您位于表单边缘时,鼠标光标也会发生变化,就像要调整大小一样,但它不允许调整大小:
I'm not looking for points here as the original poster already posted 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 instead of in answer form) gives me a
Win32Exception: Error creating window handle error.
when using theWndProc
code as it is posted there. TheShowWithoutActivation
andCreateParams
part works great to prevent activation of a form and still keep it topmost.I came up with an alternate solution to preventing a form from being clicked using
SetWindowLong
which makes the form transparent and therefore it can be clicked through andSetLayeredWindowAttributes
which sets the transparency back to normal so you can see the form again but still retain the ability to click through the form.NOTE: You -CANNOT- interact with the form at all in this state and even trying to click the 'X' button will just click whatever is behind the form at that position so you will need to use code to close the form:
I was also able to get the original approach working by making a small change to the
WndProc
code above.NOTE: This also makes the form unclickable but the behavior is different in that you can actually click the min, max and 'X' buttons but nothing happens when you do. The mouse cursor also changes when you are at the edge of the form as if to resize but it doesn't allow resizing: