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:-
- Do not resize the form in the designer. (Which is not convenient!)
- 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
- Microsoft website
- http://www.aisto.com/roeder/dotnet/
- 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 {
public class GripPanel: Panel {
private const int HTBOTTOMRIGHT = 17;
private const int WM_NCHITTEST = 0x84;
private const int WM_SIZE = 0x05;
protected override void WndProc(ref Message m) {
if (Dock == DockStyle.Bottom) {
switch (m.Msg) {
case WM_SIZE:
Invalidate();
break;
case WM_NCHITTEST:
Point p = FindForm().PointToClient(new Point((int) m.LParam));
int x = p.X;
int y = p.Y;
Rectangle rect = Bounds;
if (x >= rect.X + rect.Width - 12 &&
x <= rect.X + rect.Width &&
y >= rect.Y + rect.Height - 12 &&
y <= rect.Y + rect.Height) {
m.Result = new IntPtr(HTBOTTOMRIGHT);
return;
}
break;
}
}
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
if (Dock == DockStyle.Bottom) {
ControlPaint.DrawBorder3D(e.Graphics, ClientRectangle,
Border3DStyle.Raised, Border3DSide.Top);
ControlPaint.DrawSizeGrip(e.Graphics, BackColor,
Width - 16, Height -16, 16, 16);
}
}
}
}