¿Cómo agregar un botón extra a la barra de título de la ventana?

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 #?

ACTUALIZACIÓN : Se agregó una solución que funcionará con Aero habilitado para Windows Vista y Windows 7


Solución no Aero

El área no cliente de una interacción de ventana se gestiona mediante 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 de WM_NC * para lograr lo que desea.

Actualización: Como nunca lo intenté con .NET, obtuve unos minutos y pensé en probarlo rápidamente.

Al probar esto en Windows 7, descubrí que necesitaba deshabilitar los temas para la ventana si quería que el sistema operativo hiciera la representación básica 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; } } } } 

Por cierto. Llamé a 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.


Solución compatible con Aero

Impulsado por el comentario de @TheCodeKing, pensé en 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 se basa 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 debes 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 al á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 de forma normal, incluso sobre el marco. Incluso puedes poner controles en el área de subtítulos.

El último paso es manejar el mensaje WM_NCHITTEST, donde se comprobará la posición del cursor del mouse y se devolverá 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 .

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

Solución simple:

Paso 1: crea un formulario de Windows (esta será tu barra de título personalizada)

 -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 que desea utilizar este control personalizado en add

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

Para su constructor … puede meterse con las compensaciones que solo encajan en una buena posición para mi aplicación

Paso 3. Agregue el evento move a su formulario principal

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

Por favor, hágame saber si desea una mejor explicación de cualquiera de los códigos anteriores.