I'm attempting to make border-less forms that pop out of a tool bar. I want the user to be able to grab at the bottom-right corner (a "resize handle") and be able to resize the form, but not be able to resize or reposition the form in any other way.
I've heard that I can intercept the WM_NCHITTEST
message sent to the form and set its result to HTBOTTOMRIGHT
which will let the operating system handle the re-sizing of the form, just as if it had a sizable frame. The idea I had was to detect if the mouse pointer had entered a box I defined in the corner and if it did then return the HTBOTTOMRIGHT
result.
This doesn't quite work as I expected it to. I'm able to intercept the message, but it seems the message is only sent when the user positions the mouse cursor on the 1px thick border of the form. That means it works how I want to, if you very precisely position your cursor on the bottom-right edges.
Here is my WndProc
override:
protected override void WndProc(ref Message m)
{
const UInt32 WM_NCHITTEST = 0x0084;
const UInt32 HTBOTTOMRIGHT = 17;
const int RESIZE_HANDLE_SIZE = 40;
bool handled = false;
if (m.Msg == WM_NCHITTEST)
{
Size formSize = this.Size;
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
Rectangle hitBox = new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE);
if (hitBox.Contains(clientPoint))
{
m.Result = (IntPtr)HTBOTTOMRIGHT;
handled = true;
}
}
if (!handled)
base.WndProc(ref m);
}
Am I doing something wrong or is there a better way to do what I'm trying to do?
Much thanks.
I was looking for something similar and Anton's code was a great base. This is what I ended up to have resize work from all sides. I'm unsure a Dictionary
was optimal way to store the hitboxes, but I guess it doesn't matter all that much.
And since my form is filled with controls using Fill as Dock parameters, I just had to add a 5px padding to the Form
for it to work nicely.
protected override void WndProc(ref Message m)
{
const UInt32 WM_NCHITTEST = 0x0084;
const UInt32 WM_MOUSEMOVE = 0x0200;
const UInt32 HTLEFT = 10;
const UInt32 HTRIGHT = 11;
const UInt32 HTBOTTOMRIGHT = 17;
const UInt32 HTBOTTOM = 15;
const UInt32 HTBOTTOMLEFT = 16;
const UInt32 HTTOP = 12;
const UInt32 HTTOPLEFT = 13;
const UInt32 HTTOPRIGHT = 14;
const int RESIZE_HANDLE_SIZE = 10;
bool handled = false;
if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
{
Size formSize = this.Size;
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
Dictionary<UInt32, Rectangle> boxes = new Dictionary<UInt32, Rectangle>() {
{HTBOTTOMLEFT, new Rectangle(0, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTBOTTOM, new Rectangle(RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTBOTTOMRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, formSize.Height - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE)},
{HTRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE)},
{HTTOPRIGHT, new Rectangle(formSize.Width - RESIZE_HANDLE_SIZE, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTTOP, new Rectangle(RESIZE_HANDLE_SIZE, 0, formSize.Width - 2*RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTTOPLEFT, new Rectangle(0, 0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE) },
{HTLEFT, new Rectangle(0, RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE, formSize.Height - 2*RESIZE_HANDLE_SIZE) }
};
foreach (KeyValuePair<UInt32, Rectangle> hitBox in boxes)
{
if (hitBox.Value.Contains(clientPoint))
{
m.Result = (IntPtr) hitBox.Key;
handled = true;
break;
}
}
}
if (!handled)
base.WndProc(ref m);
}