2011-10-13 141 views
6

我有一些问题得到通知框在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上找到它。

但是,如果任何人都可以看到这样做的更优雅的方式,我们将不胜感激。

回答

2

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

这种风格应用到窗口,覆盖的CreateParams功能和更改baseParams.ExStyle。

+1

谢谢,这是完美的防止它获得焦点,它甚至不会出现在alt选项卡现在这是真棒。我仍然存在的一个小问题是TopMost = true似乎覆盖了ShowWithoutActivation,所以当调用form.Show()时它仍然获得焦点。有没有办法解决? – yebetrollin

+0

查看Ziketo对此的回应。还记得投票赞成有帮助的人们的问题。 –

3

在WPF试试这个:

ShowActivated="False" 
0

我不找点这里原来的海报已经张贴为他们工作一个解决方案,但我想分享一下我这个问题的经验。使用上面的解决方案(在问题的底部而不是答案形式)给我一个Win32Exception: Error creating window handle error.当使用WndProc代码,因为它是张贴在那里。 ShowWithoutActivationCreateParams部分非常适合阻止表单的激活并保持最高级。

我想出了一个替代的解决方案,以防止窗体从使用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; 
    } 
}