Serialcoder en Français Serialcoder in English
TEL : +33 (0)9 72 13 15 17

Windows Forms FAQ resources

2. Windows Forms Controls

2.9 How do I provide a 1 pixel border in the NonClient area of my Control?


You will have to first provide some space in the NC area by setting the WS_BORDER flag in CreateParams and then draw the border yourself by listening to the WM_NCPAINT message in your Control, as follows:


          protected override CreateParams CreateParams
          {
               get
               {
                    System.Windows.Forms.CreateParams cp = base.CreateParams;
                    if(this.needFlatBorder)
                    {
                         cparams.ExStyle &= ~512 /*WS_EX_CLIENTEDGE*/;
                         cparams.Style &= ~8388608 /*WS_BORDER*/;
                         cp.Style |= 0x800000; // WS_BORDER
                    }
               }
          }

          protected override void WndProc(ref Message m)
          {
               if(m.Msg == 133/*WM_NCPAINT*/)
               {
                    this.DrawFlatNCBorder(ref m);
               }
               base.WndProc(ref m);
          }

          private void DrawFlatNCBorder(ref Message msg)
          {
               IntPtr hRgn1 = (IntPtr) msg.WParam;
               // The update region is clipped to the window frame. When wParam is 1, the entire window frame needs to be updated.

               IntPtr hdc = NativeMethods.GetDCEx(msg.HWnd, hRgn1, 1/*DCX_WINDOW*/|0x0020/*DCX_PARENTCLIP*/);
               if (hdc != IntPtr.Zero)
               {
                    using (Graphics g = Graphics.FromHdc(hdc))
                    {

                         Rectangle bounds = new Rectangle(0,0,this.Width,this.Height);
               
                         ControlPaint.DrawBorder(g,bounds,this.borderColor,ButtonBorderStyle.Solid);

                         // create a clipping region for remaining parts to be drawn excluding
                         // the border we did just drew
                         bounds.Inflate(-1, -1);
                         IntPtr hRgn2 = NativeMethods.CreateRectRgn(bounds.Left, bounds.Top, bounds.Right, bounds.Bottom);

                         if(hRgn2 == (IntPtr)1)
                         {
                              // Provide a new clipping region.
                              msg.WParam = (IntPtr) hRgn2;
                         }
                         else
                         {
                              // combine with existing clipping region.
                              NativeMethods.CombineRgn(hRgn1, hRgn1, hRgn2, NativeMethods.RGN_AND);
                              NativeMethods.DeleteObject(hRgn2);
                         }
                    }

                    msg.Result = (IntPtr) 1;
                    NativeMethods.ReleaseDC(msg.HWnd, hdc);
          
               }               
               Invalidate();
          }