2010-05-15 20 views
32

He visto que algunas aplicaciones (tal vez no .NET) que tienen un botón adicional a la izquierda del botón de minimizar en la barra de título del formulario? ¿Cómo puedo lograr esto en C#?¿Cómo agregar un botón adicional a la barra de título de la ventana?

+2

Tenga en cuenta que no debe hacer esto: http://msdn.microsoft.com/en-us/library/aa974173(v=MSDN.10).aspx - »No agregue controles a un marco de ventana. Ponga los controles dentro de la ventana en su lugar. « – Joey

+3

@Johannes, admito que a veces las aplicaciones que hacen esto pueden ser molestas, pero algunas aplicaciones lo hacen muy bien. Como Ultramon, que pone un botón para intercambiar monitores en el marco que parece muy nativo en Windows 7. Por lo tanto, hay aplicaciones que lo hacen con buen gusto. – Josh

+3

@Josh: Office 2007 también lo hizo (pero esa no es la primera vez que el equipo de Office ignora las Pautas de Windows UX). Aún así, ese documento está escrito por profesionales de UX y los simples desarrolladores (o monos de código) están trabajando duro para ignorarlos. Y por el bien de los usuarios, creo que deberían seguirlo, a menos que puedan traer personas con experiencia y conocimiento y, lo más importante, pruebas de usabilidad, para probar lo contrario. – Joey

Respuesta

32

ACTUALIZACIÓN: Se ha añadido una solución que funcione con Aero activado para Windows Vista y Windows 7


Aero no Solución

El área no cliente de una interacción ventana es administrado por una serie de mensajes específicos no cliente. Por ejemplo, el mensaje WM_NCPAINT se envía al procedimiento de ventana para pintar el área no cliente.

Nunca he hecho esto desde .NET, pero sospecho que puede sobreescribir el WndProc y manejar los mensajes WM_NC * para lograr lo que quiere.

Actualización: Como nunca lo intenté con .NET, tuve unos minutos y pensé en probarlo.

Probando esto en Windows 7, encontré que necesitaba deshabilitar los temas para la ventana si quería que el sistema operativo hiciera la representación base del área no cliente. Así que aquí hay una breve prueba. Utilicé GetWindowDC para obtener el DC de toda la ventana en lugar de GetDCEx, simplemente porque podía interoperar desde la memoria y no había buscado todas las constantes del distintivo para GetDcEx. Y, por supuesto, el código podría hacer con más comprobación de errores.

using System; 
using System.Drawing; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

namespace WindowsFormsApplication1 
{ 
    public partial class CustomBorderForm : Form 
    { 
    const int WM_NCPAINT = 0x85; 

    [DllImport("user32.dll", SetLastError = true)] 
    public static extern IntPtr GetWindowDC(IntPtr hwnd); 

    [DllImport("user32.dll", SetLastError = true)] 
    public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc); 

    [DllImport("user32.dll", SetLastError = true)] 
    public static extern void DisableProcessWindowsGhosting(); 

    [DllImport("UxTheme.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
    public static extern IntPtr SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList); 

    public CustomBorderForm() 
    { 
     // This could be called from main. 
     DisableProcessWindowsGhosting(); 

     InitializeComponent(); 
    } 

    protected override void OnHandleCreated(EventArgs e) 
    { 
     SetWindowTheme(this.Handle, "", ""); 
     base.OnHandleCreated(e); 
    } 

    protected override void WndProc(ref Message m) 
    { 
     base.WndProc(ref m); 

     switch (m.Msg) 
     { 
     case WM_NCPAINT: 
      { 
      IntPtr hdc = GetWindowDC(m.HWnd); 
      using (Graphics g = Graphics.FromHdc(hdc)) 
      { 
       g.FillEllipse(Brushes.Red, new Rectangle((Width-20)/2, 8, 20, 20)); 
      } 
      int r = ReleaseDC(m.HWnd, hdc); 
      } 
      break; 
     } 
    } 
    } 
} 

Btw. Llamé al DisableProcessWindowsGhosting, esto impedirá que el sistema operativo dibuje el área no cliente si la aplicación tarda demasiado en responder a los mensajes de Windows. Si no haces esto, entonces en algunas situaciones el borde se renderizará pero tus adornos no se mostrarán. De modo que eso depende de sus requisitos, que sea adecuado para usted o no.


Aero solución

Impulsada por el comentario de @TheCodeKing apoyado, pensé que iba a echar otro vistazo a esto. Resulta que esto se puede hacer de una manera completamente documentada mientras se admite Aero. Pero no es para los débiles de corazón. No proporcionaré una solución completa aquí, todavía hay algunas fallas en el entrenamiento, pero tiene los aspectos básicos.

Este código/solución está basada en el ejemplo de Win32 que se puede encontrar en la siguiente ubicación http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx

En principio lo que hay que hacer es lo siguiente.

  • Extienda el área del cliente de la ventana para cubrir el Marco. Esto se hace manejando el mensaje WM_NCCALCSIZE y devolviendo 0. Esto le da al área No Cliente un tamaño de 0 y por lo tanto el área del cliente ahora cubre toda la ventana.
  • Extienda el marco en el área del cliente usando DwmExtendFrameIntoClientArea. Esto hace que el sistema operativo renderice el cuadro sobre el área del cliente.

Los pasos anteriores le darán una ventana con el marco de cristal estándar excluyendo el menú del sistema (Icono de la ventana) y el título.Los botones de minimizar, maximizar y cerrar aún se dibujarán y funcionarán. Lo que no podrá hacer es arrastrar o cambiar el tamaño de la ventana, esto se debe a que el marco no está realmente allí, recuerde que el área del cliente cubre toda la ventana, le hemos pedido al sistema operativo que dibuje el marco en el área del cliente.

Ahora puede dibujar en la ventana normalmente, incluso en la parte superior del marco. Incluso puedes poner controles en el área de subtítulos.

El último paso es manejar el mensaje WM_NCHITTEST, donde se verificará la posición del cursor del mouse y se mostrará un indicador que indica a las ventanas en qué área de la ventana se encuentra el mouse. Por ejemplo, si devuelve HT_CAPTION, la ventana actuará como si el mouse estuviera sobre el título, y le permitirá arrastrar la ventana, etc. Es esta función la que requerirá una implementación completa para tener una ventana completamente funcional con un marco personalizado .

+0

Desafortunadamente, esta técnica no funciona con el vidrio aerodinámico. Me interesaría si alguien sabe que el vudú requiere trabajar en vidrio. Hasta ahora no he encontrado ningún ejemplo de trabajo. – TheCodeKing

+0

@TheCodeKing, he actualizado la respuesta para que cubra su pregunta. También hice un prototipo de esto en .NET solo para confirmar que se puede hacer funcionar. Aquí está el artículo de MSDN al que hice referencia http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx –

+0

En realidad, eso me recuerda que sé cómo hacer esto, tengo algún código en alguna parte , simplemente no representaba en el modo de diseño cómo se veía en el tiempo de ejecución.¿Tiene un ejemplo C# de la técnica que ha descrito, he intentado más y no funcionan? – TheCodeKing

4

Creo que una forma de hacerlo sería manejar el mensaje WM_NCPAINT (pintura no cliente) para dibujar el botón, y manejar clics del mouse no cliente para saber que alguien hizo clic en el "botón".

3

solución simple:

Paso 1: Crear un formulario Windows (este será su barra de título personalizado)

-Set Form Border Style to None 
-Add whatever controls you would like to this 
-I will name this custom form "TitleBarButtons" 

Paso 2. En el de que desea utilizar este control personalizado en el complemento

titleBarBtn = new TitleBarButtons(); 
titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5); 
titleBarBtn.Show(); 
titleBarBtn.Owner = this; 

para su constructor ... se puede ensuciar con las compensaciones esto sólo cabe en una posición agradable para mi aplicación

Paso 3. Añadir el evento se trasladan a su forma principal

private void Form14_Move(object sender, EventArgs e) 
{ 
    titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5); 
} 

Por favor, hágamelo saber si le gustaría una mejor explicación de cualquiera de los código de seguridad.

+0

¿Qué es un TitleBarButtons? No puedo encontrar ninguna referencia de esta clase. – Borik

+0

Lo siento, me di cuenta de que no estaba claro. Creo que "TitleBarButtons" fue la forma personalizada creada en el primer paso. Revisaré mi respuesta para que quede más clara. – hrh

+0

@hrh hilo muy viejo, aunque es posible en wpf también, agregando formulario a la ventana wpf – murmansk

Cuestiones relacionadas