2011-10-13 13 views
6

Tengo algunos problemas para que un cuadro de notificación se comporte correctamente en C#. Básicamente estoy mostrando una forma sin boar en la parte inferior derecha de la pantalla, que muestra un mensaje durante unos segundos y luego desaparece. El problema es que necesito que aparezca encima de otras ventanas sin que sea capaz de robar el foco. Idealmente, quiero que sea un código puramente administrado, aunque al mirar ejemplos similares dudo que esto sea posible.Ventana de notificación: evitar que la ventana se vuelva a enfocar

En este momento estoy evitando que el robo del foco cuando se llama a Form.Show() con una anulación:

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

y luego haciendo caso omiso de clics del ratón con:

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

Sin embargo me parece que si los utilizo junto con TopMost = true (que necesito), gana foco de todos modos, y si todas las demás ventanas se minimizan, también gana foco.

Entonces, ¿hay alguna forma de evitar que un formulario se vuelva enfocado (ya sea con un clic del mouse, alt-tab, etc.), mientras sigue siendo el top/top superior? Incluso el solo hecho de enfocarse de inmediato en la ventana desde la que se lo robó funcionaría (aunque introduciría el parpadeo).

Cualquier sugerencia sería muy apreciada, realmente estoy atrapado en esto.

EDIT:

Ok, así que finalmente me las arreglé para conseguir este trabajo usando:

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

     } 
    } 

También he añadido Form.Hide() para el evento GotFocus de manera que incluso si de alguna manera obtener un enfoque , simplemente se cierra y se sale de la forma de los usuarios lo antes posible.

Además, si alguien se pregunta, las constantes para todos los estilos de ventana, etc. se pueden encontrar en WINUSER.H, está en línea en http://www.woodmann.com/fravia/sources/WINUSER.H si no puede encontrarlo.

Sin embargo, si alguien puede ver una forma más elegante de hacerlo, se lo agradecería.

Respuesta

2

Posiblemente el estilo de ventana extendida WS_EX_NOACTIVATE es lo que está buscando. La ventana con este estilo no se activa cuando se hace clic. Por ejemplo, la ventana Teclado virtual tiene este estilo.

Para aplicar este estilo a la ventana, anule la función CreateParams y cambie baseParams.ExStyle.

+1

Gracias, esto es perfecto para evitar que se enfoque, ni siquiera aparece en alt-tab ahora, que es increíble. El único problema menor que todavía tengo es que el TopMost = true parece anular ShowWithoutActivation, por lo que aún gana foco cuando se llama a form.Show(). ¿Hay alguna forma de evitar esto? – yebetrollin

+0

Vea la respuesta de Ziketo para eso. También recuerde votar las preguntas de las personas que ayudaron. –

3

En WPF intenta esto:

ShowActivated="False" 
0

No estoy en busca de puntos aquí como el creador original ya se registró una solución que funcionó para ellos, pero quería compartir mi experiencia con este problema. El uso de la solución anterior (que está en la parte inferior de la pregunta en lugar de en forma de respuesta) me da un Win32Exception: Error creating window handle error. cuando se usa el código WndProc tal como está publicado allí. La pieza ShowWithoutActivation y CreateParams funciona de maravilla para evitar la activación de un formulario y aún así mantenerlo en la parte superior.

me ocurrió una solución alternativa a la prevención de una forma de que se ha pulsado el uso SetWindowLong lo que hace que la forma transparente y por lo tanto, se puede hacer clic a través y SetLayeredWindowAttributes que establece la transparencia de vuelta a la normalidad para que pueda ver el formulario de nuevo, pero todavía retiene la capacidad de hacer clic a través del formulario.

NOTA: Usted -CANNOT- interactuar con la forma en absoluto en este estado e incluso tratando de hacer clic en el botón 'X' será simplemente haga clic en todo lo que sea detrás de la forma en esa posición por lo que tendrá que utilizar código para cerrar el formulario:

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

también era capaz de conseguir el enfoque original de trabajo haciendo un pequeño cambio en el código WndProc anteriormente.

NOTA: Esto también hace que la forma unclickable pero el comportamiento es diferente, ya que en realidad se puede hacer clic en el mínimo, máximo y los botones 'X', pero no pasa nada cuando lo hace. El cursor del mouse también cambia cuando está en el borde del formulario como para cambiar el tamaño, pero no permite cambiar el tamaño:

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