Drawing my own title bar

2019-03-03 11:05发布

I'm drawing part of the title bar in my WinForm applications. Is working Ok (placing the name of the company centered and in Orange)

enter image description here

This is the code to do that at the form code:

using System.Runtime.InteropServices;


  [DllImport("user32.dll")]
  private static extern IntPtr GetWindowDC(IntPtr hWnd);

  [DllImport("user32.dll")]
  private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);



  private Rectangle PosicionTexto()
  {
     string sTexto = String.Format("{0} : {1}", this.Text, "Trevo I.T.");
     sTexto = "Trevo I.T.";

     Graphics oG = this.CreateGraphics();

     SizeF oS = oG.MeasureString(sTexto, new Font("Tahoma", 8f, FontStyle.Bold));

     Rectangle rect = new Rectangle();
     rect.X = (this.Width/2) - (int)(oS.Width/2);
     rect.Y = SystemInformation.FrameBorderSize.Height + 4;
     rect.Width = (int)oS.Width;
     rect.Height = SystemInformation.CaptionButtonSize.Height;

     return rect;
  }

  private void DrawTitleBar(IntPtr hwnd)
  {
     // Obtenemos el Canvas a partir del DC de la ventana
     IntPtr hdc = GetWindowDC(hwnd);
     Graphics gBarra = Graphics.FromHdc(hdc);

     Rectangle rect = PosicionTexto();
     string sTexto = String.Format("{0} : {1}", this.Text, "Trevo I.T.");
     sTexto = "Trevo I.T.";
     gBarra.DrawString(sTexto, new Font("Tahoma", 8f, FontStyle.Bold), Brushes.Orange, new PointF(rect.X, rect.Y));

     // Liberamos la memoria*/
     gBarra.Flush();
     ReleaseDC(hwnd, hdc);
  }

  protected override void WndProc(ref Message m)
  {
     switch (m.Msg)
     {
        case 0x000F: // WM_PAINT
        case 0x0085: // WM_NCPAINT:
           base.WndProc(ref m);
           DrawTitleBar(m.HWnd);
           break;

        default:
           // Invocamos a la funcion original
           base.WndProc(ref m);
           break;
     }
  }

The issue is resizing the window. The text is not cleared and shown several times. Is there a way to "Refresh" the title bar before drawing my text?

Thanks in advance.

EDIT Almost solved. Added two more methods:

  // Rectangle of the title bar
  private Rectangle TitleBarRectangle()
      {
         Rectangle rect = new Rectangle();
         rect.X = 1 + SystemInformation.CaptionButtonSize.Width;  // Avoid to draw over the icon
         rect.Y = 1;
         rect.Width = Width - (SystemInformation.CaptionButtonSize.Width * 4); // Avoid to draw over the buttons
         rect.Height = SystemInformation.CaptionButtonSize.Height;
         return rect;
      }

  // Draw a filled rectangle
  private void ClearTitleBar(IntPtr hwnd)
  {
     // Obtenemos el Canvas a partir del DC de la ventana
     IntPtr hdc = GetWindowDC(hwnd);

     try
     {
        using (Graphics gBarra = Graphics.FromHdc(hdc))
        {
           Rectangle rect = TitleBarRectangle();
           gBarra.FillRectangle(new SolidBrush(SystemColors.ActiveCaption), rect);
           gBarra.Flush();
        }
     }
     finally
     {
        ReleaseDC(hwnd, hdc);
     }
  }

and modified one:

  protected override void WndProc(ref Message m)
  {
     switch (m.Msg)
     {
        case 0x0085: // WM_NCPAINT:
        case 0x0005: // WM_SIZE
           base.WndProc(ref m);
           DrawTitleBar(m.HWnd);
           break;

        case 0x214:  //   WM_SIZING 
           base.WndProc(ref m);
           ClearTitleBar(m.HWnd);
           break;

        default:
           // Invocamos a la funcion original
           base.WndProc(ref m);
           break;
     }
  }

By the way, the whole idea started from Copstone

Thanks Lars, Zach and of course, Jim. Your comments and answers put me on the way.

1条回答
欢心
2楼-- · 2019-03-03 11:37

A simple solution is to call gBarra.FillRectangle to fill the title bar with the background color before you draw the string.

Are you sure you need to do this in WM_PAINT? Seems like WM_NCPAINT would be enough.

I strongly recommend that you use try...finally to ensure that the DC gets released. That is:

private void DrawTitleBar(IntPtr hwnd)
{
     // Obtenemos el Canvas a partir del DC de la ventana
     IntPtr hdc = GetWindowDC(hwnd);
     try
     {
         using (Graphics gBarra = Graphics.FromHdc(hdc))
         {
             Rectangle rect = PosicionTexto();
             string sTexto = String.Format("{0} : {1}", this.Text, "Trevo I.T.");
             sTexto = "Trevo I.T.";
             gBarra.DrawString(sTexto, new Font("Tahoma", 8f, FontStyle.Bold), Brushes.Orange, new PointF(rect.X, rect.Y));

             // Liberamos la memoria*/
             gBarra.Flush();
         }   
     }
     finally
     {
         ReleaseDC(hwnd, hdc);
     }
}
查看更多
登录 后发表回答