2011-10-13 193 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; 
    } 
}