visual una studio para metodo lineas linea librerias hacer flechas drawline dibujar crear como winforms winapi windows-vista wndproc titlebar

winforms - una - librerias para dibujar en c#



¿Cómo dibujar un botón personalizado en Window Titlebar con Windows Forms? (3)

¿Cómo se dibuja un botón personalizado al lado de los botones minimizar, maximizar y cerrar dentro de la barra de título del formulario?

Sé que necesitas utilizar las llamadas a la API de Win32 y anular el procedimiento de WndProc, pero no he podido encontrar una solución que funcione correctamente.

¿Alguien sabe como hacer esto? Más específicamente, ¿alguien sabe cómo hacer esto que funciona en Vista?


Dibujar parece ser la parte fácil, lo siguiente hará eso:

[Editar: Código eliminado, ver mi otra respuesta]

El problema real es cambiar el estado y detectar clics en el botón ... para eso tendrá que conectar el controlador de mensajes global para el programa, .NET parece ocultar los eventos del mouse para un formulario mientras no está en el contenedor real áreas (es decir, el mouse se mueve y hace clic en la barra de título). Estoy buscando información sobre eso, lo encontré ahora, estoy trabajando en ello, no debería ser demasiado difícil ... Si podemos averiguar lo que estos mensajes realmente están pasando.


Sé que ha pasado mucho tiempo desde la última respuesta, pero esto realmente me ayudó recientemente y me gusta actualizar el código proporcionado por Chris con mis comentarios y modificaciones. La versión funciona perfectamente en Win XP y Win 2003. En Win 2008 ot tiene un pequeño error que no pude identificar al cambiar el tamaño de Windows. También funciona en Vista (sin Aero), pero tenga en cuenta que los botones de la barra de título no son cuadrados y las dimensiones de los botones deben tener esto en cuenta.

switch (m.Msg) { // WM_NCPAINT / WM_PAINT case 0x85: case 0x0A: //Call base method base.WndProc(ref m); //we have 3 buttons in the corner of the window. So first''s new button left coord is offseted by 4 widths int crt = 4; //navigate trough all titlebar buttons on the form foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) { //Calculate button coordinates p.X = (Bounds.Width - crt * crtBtn.Size.Width); p.Y = (Bounds.Height - ClientRectangle.Height - crtBtn.Size.Height) / 2; //Initialize button and draw crtBtn.Location = p; crtBtn.ButtonState = ImageButtonState.NORMAL; crtBtn.DrawButton(m.HWnd); //increment button left coord location offset crt++; } m.Result = IntPtr.Zero; break; // WM_ACTIVATE case 0x86: //Call base method base.WndProc(ref m); //Draw each button foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) { crtBtn.ButtonState = ImageButtonState.NORMAL; crtBtn.DrawButton(m.HWnd); } break; // WM_NCMOUSEMOVE case 0xA0: //Get current mouse position p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits p.Y = (int)m.LParam >> 16; // Extract the most significant 16 bits p.X -= windowRect.Left; p.Y -= windowRect.Top; //Call base method base.WndProc(ref m); ImageButtonState newButtonState; foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) { if (crtBtn.HitTest(p)) {//mouse is over the current button if (crtBtn.MouseButtonState == MouseButtonState.PRESSED) //button is pressed - set pressed state newButtonState = ImageButtonState.PRESSED; else //button not pressed - set hoover state newButtonState = ImageButtonState.HOOVER; } else { //mouse not over the current button - set normal state newButtonState = ImageButtonState.NORMAL; } //if button state not modified, do not repaint it. if (newButtonState != crtBtn.ButtonState) { crtBtn.ButtonState = newButtonState; crtBtn.DrawButton(m.HWnd); } } break; // WM_NCLBUTTONDOWN case 0xA1: //Get current mouse position p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits p.Y = (int)m.LParam >> 16; // Extract the most significant 16 bits p.X -= windowRect.Left; p.Y -= windowRect.Top; //Call base method base.WndProc(ref m); foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) { if (crtBtn.HitTest(p)) { crtBtn.MouseButtonState = MouseButtonState.PRESSED; crtBtn.ButtonState = ImageButtonState.PRESSED; crtBtn.DrawButton(m.HWnd); } } break; // WM_NCLBUTTONUP case 0xA2: case 0x202: //Get current mouse position p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits p.Y = (int)m.LParam >> 16; // Extract the most significant 16 bits p.X -= windowRect.Left; p.Y -= windowRect.Top; //Call base method base.WndProc(ref m); foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) { //if button is press if (crtBtn.ButtonState == ImageButtonState.PRESSED) { //Rasie button''s click event crtBtn.OnClick(EventArgs.Empty); if (crtBtn.HitTest(p)) crtBtn.ButtonState = ImageButtonState.HOOVER; else crtBtn.ButtonState = ImageButtonState.NORMAL; } crtBtn.MouseButtonState = MouseButtonState.NOTPESSED; crtBtn.DrawButton(m.HWnd); } break; // WM_NCHITTEST case 0x84: //Get current mouse position p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits p.Y = (int)m.LParam >> 16; // Extract the most significant 16 bits p.X -= windowRect.Left; p.Y -= windowRect.Top; bool isAnyButtonHit = false; foreach (TitleBarImageButton crtBtn in titleBarButtons.Values) { //if mouse is over the button, or mouse is pressed //(do not process messages when mouse was pressed on a button) if (crtBtn.HitTest(p) || crtBtn.MouseButtonState == MouseButtonState.PRESSED) { //return 18 (do not process further) m.Result = (IntPtr)18; //we have a hit isAnyButtonHit = true; //return break; } else {//mouse is not pressed and not over the button, redraw button if needed if (crtBtn.ButtonState != ImageButtonState.NORMAL) { crtBtn.ButtonState = ImageButtonState.NORMAL; crtBtn.DrawButton(m.HWnd); } } } //if we have a hit, do not process further if (!isAnyButtonHit) //Call base method base.WndProc(ref m); break; default: //Call base method base.WndProc(ref m); //Console.WriteLine(m.Msg + "(0x" + m.Msg.ToString("x") + ")"); break; }

El código demuestra los mensajes que deben tratarse y cómo tratarlos. El código usa una colección de objetos personalizados TitleBarButton. Esa clase es demasiado grande para ser incluida aquí pero puedo proporcionarla si es necesario junto con un ejemplo.


Lo siguiente funcionará en XP, no tengo a mano una máquina de Vista para probarlo, pero creo que sus problemas provienen de un hWnd incorrecto de alguna manera. De todos modos, sigue con el código mal comentado.

// The state of our little button ButtonState _buttState = ButtonState.Normal; Rectangle _buttPosition = new Rectangle(); [DllImport("user32.dll")] private static extern IntPtr GetWindowDC(IntPtr hWnd); [DllImport("user32.dll")] private static extern int GetWindowRect(IntPtr hWnd, ref Rectangle lpRect); [DllImport("user32.dll")] private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); protected override void WndProc(ref Message m) { int x, y; Rectangle windowRect = new Rectangle(); GetWindowRect(m.HWnd, ref windowRect); switch (m.Msg) { // WM_NCPAINT case 0x85: // WM_PAINT case 0x0A: base.WndProc(ref m); DrawButton(m.HWnd); m.Result = IntPtr.Zero; break; // WM_ACTIVATE case 0x86: base.WndProc(ref m); DrawButton(m.HWnd); break; // WM_NCMOUSEMOVE case 0xA0: // Extract the least significant 16 bits x = ((int)m.LParam << 16) >> 16; // Extract the most significant 16 bits y = (int)m.LParam >> 16; x -= windowRect.Left; y -= windowRect.Top; base.WndProc(ref m); if (!_buttPosition.Contains(new Point(x, y)) && _buttState == ButtonState.Pushed) { _buttState = ButtonState.Normal; DrawButton(m.HWnd); } break; // WM_NCLBUTTONDOWN case 0xA1: // Extract the least significant 16 bits x = ((int)m.LParam << 16) >> 16; // Extract the most significant 16 bits y = (int)m.LParam >> 16; x -= windowRect.Left; y -= windowRect.Top; if (_buttPosition.Contains(new Point(x, y))) { _buttState = ButtonState.Pushed; DrawButton(m.HWnd); } else base.WndProc(ref m); break; // WM_NCLBUTTONUP case 0xA2: // Extract the least significant 16 bits x = ((int)m.LParam << 16) >> 16; // Extract the most significant 16 bits y = (int)m.LParam >> 16; x -= windowRect.Left; y -= windowRect.Top; if (_buttPosition.Contains(new Point(x, y)) && _buttState == ButtonState.Pushed) { _buttState = ButtonState.Normal; // [[TODO]]: Fire a click event for your button // however you want to do it. DrawButton(m.HWnd); } else base.WndProc(ref m); break; // WM_NCHITTEST case 0x84: // Extract the least significant 16 bits x = ((int)m.LParam << 16) >> 16; // Extract the most significant 16 bits y = (int)m.LParam >> 16; x -= windowRect.Left; y -= windowRect.Top; if (_buttPosition.Contains(new Point(x, y))) m.Result = (IntPtr)18; // HTBORDER else base.WndProc(ref m); break; default: base.WndProc(ref m); break; } } private void DrawButton(IntPtr hwnd) { IntPtr hDC = GetWindowDC(hwnd); int x, y; using (Graphics g = Graphics.FromHdc(hDC)) { // Work out size and positioning int CaptionHeight = Bounds.Height - ClientRectangle.Height; Size ButtonSize = SystemInformation.CaptionButtonSize; x = Bounds.Width - 4 * ButtonSize.Width; y = (CaptionHeight - ButtonSize.Height) / 2; _buttPosition.Location = new Point(x, y); // Work out color Brush color; if (_buttState == ButtonState.Pushed) color = Brushes.LightGreen; else color = Brushes.Red; // Draw our "button" g.FillRectangle(color, x, y, ButtonSize.Width, ButtonSize.Height); } ReleaseDC(hwnd, hDC); } private void Form1_Load(object sender, EventArgs e) { _buttPosition.Size = SystemInformation.CaptionButtonSize; }