Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

GripPanel

0.00/5 (No votes)
18 Oct 2003 1  
A WinForms Panel that shows a size grip when docked at the bottom of a form.

Introduction

This is not an earth-shattering new development but you might find a use for it in your toolbox.

It is simply a control derived from the standard Winforms Panel control that draws a sizing grip when the panel is docked at the bottom of a form.

Background

This control was written to get around a bug in Winforms that prevents controls anchored to the right and bottom of a form working correctly in a derived form.

I simply wanted a base form with OK and Cancel buttons anchored to the bottom right of the form but unfortunately, when a derived form was resized in the designer, the next compile moved the buttons to incorrect positions.

This is documented in Microsoft Knowledge Base Article 316560[1] but the workarounds are:-

  1. Do not resize the form in the designer. (Which is not convenient!)
  2. Change the modifier from private to protected. (Which my controls already were, so this is incorrect)

My solution to this problem was to add a panel, all docked to the bottom of the form, with the required buttons (and some empty labels to provide spacing) docked to the right within that panel. This worked fine but it means that the form sizing grip was no longer visible.

So I decided to write a derived Panel that would have a sizing grip! What should have been a simple 5-minute job left me burning the midnight oil determined to find out how to do this.

How it works

I used Lutz Roeder's excellent Reflector for .NET[2] to look into System.Windows.Forms.Dll to see how Microsoft did it for a form and I also found an article by Karl E. Peterson called "Get a Grip With SubClassing"[3] written for VB which does a similar thing.

The first part, drawing the size grip itself is easy. ControlPaint.DrawSizeGrip (part of WinForms) will draw a size grip of any size onto a Graphics object such as that provided in the PaintEventArgs parameter the OnPaint method.

The next bit was to make the cursor change shape and actually resize the form when the mouse pointer is over the size grip. To do this, I had to override the WndProc method and look for WM_NCHITTEST message. When this message is received, I check whether the mouse pointer is within the size grip and if so, basically lie to Windows and tell it that the mouse pointer is in the lower-right corner of a border of a resizable window. At this point, Windows takes over and does all that is necessary!

Finally, when the window is being resized, I need to invalidate the rectangle containing the size grip so that it will be redrawn and not leave 'bits' behind. At first, I Invalidated just the rectangle containing the grip but when the form was resized quickly, some bits were still left behind. Using Reflector again, I found that Microsoft took the easy option and Invalidated the whole control so I did the same!

Oh, and I also added a dividing line at the top of the panel using ControlPaint in OnPaint again. This is optional.

References

  1. Microsoft website
  2. http://www.aisto.com/roeder/dotnet/
  3. http://archive.devx.com/premier/mgznarch/vbpj/1999/06jun99/ap0699.pdf

Source code

Here is the source code in full:

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

namespace SimmoTech.Forms {
  /// <summary>

  /// This control works exactly the same as a normal

  /// panel but, if the panel is docked at the bottom of the form,

  /// then a sizing grip is added and a horizontal 3D line

  /// is added at the top of panel.

  /// </summary>

  public class GripPanel: Panel {

    private const int HTBOTTOMRIGHT = 17;
    private const int WM_NCHITTEST = 0x84;
    private const int WM_SIZE = 0x05;

    /// <summary>

    /// Catch some windows messages

    /// </summary>

    /// <param name="m"></param>

    protected override void WndProc(ref Message m) {

      // Only catch messages if the panel is docked to the bottom

      if (Dock == DockStyle.Bottom) {

        switch (m.Msg) {

          // If the panel is being resized then we

          // need to redraw its contents

          case WM_SIZE:
            Invalidate();
            break;

          // If the system is asking where the mouse pointer is,

          // we need to check whether it is over our sizing grip

          case WM_NCHITTEST:

            // Convert to client co-ordinates of parent

            Point p = FindForm().PointToClient(new Point((int) m.LParam));
            int x = p.X;
            int y = p.Y;
            Rectangle rect = Bounds;

            // Is the mouse pointer over our sizing group?

            // (Use 12 pixels rather than 16 otherwise

            // too large an area is checked)

            if (x >= rect.X + rect.Width - 12 && 
                x <= rect.X + rect.Width && 
                y >= rect.Y + rect.Height - 12 && 
                y <= rect.Y + rect.Height) {

              // Yes, so tell windows it is in the lower-right

              // corner of a border of a resizable window

              // Windows will then do the neccessary resizing

              m.Result = new IntPtr(HTBOTTOMRIGHT);
              return;
            }
            break;
        }
      }

      // Do the normal message handling

      base.WndProc(ref m);
    }

    /// <summary>

    /// Override to add the border line at the top

    /// of the panel and the size grip itself

    /// </summary>

    /// <param name="e"></param>

    protected override void OnPaint(PaintEventArgs e) {

      // Do the normal painting

      base.OnPaint(e);
      // Are we docked at the bottom?

      if (Dock == DockStyle.Bottom) {

          // Yes, so paint the adornments

          ControlPaint.DrawBorder3D(e.Graphics, ClientRectangle,
                   Border3DStyle.Raised, Border3DSide.Top);
          ControlPaint.DrawSizeGrip(e.Graphics, BackColor, 
                   Width - 16, Height -16, 16, 16);
      }
    }
  }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here