我遇到一些问题,让通知框在c#中正常运行。基本上我在屏幕的右下方显示一个无边框形式,它会显示一条消息几秒钟然后消失。问题是,我需要它出现在其他窗口的顶部,而不能偷走焦点。理想情况下,我希望它是纯粹的托管代码,虽然通过类似的例子我怀疑这是可能的。
目前,当我使用覆盖调用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在线找到。
然而,如果有人能够看到更优雅的方式,我们将不胜感激。
答案 0 :(得分:3)
在WPF中试试这个:
ShowActivated="False"
答案 1 :(得分:2)
可能WS_EX_NOACTIVATE扩展窗口样式是您正在寻找的。单击时不激活具有此样式的窗口。例如,“虚拟键盘”窗口具有此样式。
要将此样式应用于窗口,请覆盖CreateParams函数并更改baseParams.ExStyle。
答案 2 :(得分:0)
我不在这里寻找积分,因为原始海报已经发布了一个适合他们的解决方案,但我想分享我对这个问题的经验。使用上面的解决方案(位于问题的底部而不是答案形式),在使用Win32Exception: Error creating window handle error.
代码时,会给我WndProc
。 ShowWithoutActivation
和CreateParams
部分可以很好地阻止表单的激活并仍然保持最顶层。
我提出了另一种解决办法,可以防止使用SetWindowLong
点击表单,这样可以使表单透明,因此可以点击它并SetLayeredWindowAttributes
将透明度恢复正常,这样您就可以了可以再次看到该表单但仍保留单击表单的功能。
注意:您-CANNOT-在此状态下与表单完全互动,甚至尝试点击“' 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'按钮,但没有任何事情发生。当您处于表单边缘时,鼠标光标也会发生变化,就像调整大小但不允许调整大小一样:
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;
}
}