Custom Resize Handle in Border-less Form C#

自古美人都是妖i 提交于 2019-11-27 14:22:22
Charles P.

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);
}

just little modification to your code. I've added WM_MOUSEMOVE message handling:

    protected override void WndProc(ref Message m)
    {
        const UInt32 WM_NCHITTEST = 0x0084;
        const UInt32 WM_MOUSEMOVE = 0x0200;
        const UInt32 HTBOTTOMRIGHT = 17;
        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);
            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);
    }

by the way, you can draw system specific window size grip with ControlPaint.DrawSizeGrip Method http://msdn.microsoft.com/en-us/library/2e1yx2sa.aspx

Anton Semenov, i didn't understand your code.

Anyway, i had a problem with the first code of Charles P,
when i maximize the window and then try to change its size - it is being resized.
after that i couldn't fix it again to its normal size, nor maximize it again with the normal max button.

what i did to fix this problem was adding condition inside the 'foreach' loop at the bottom:

    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 (this.WindowState != FormWindowState.Maximized 
                    && hitBox.Value.Contains(clientPoint))
                    {
                        m.Result = (IntPtr)hitBox.Key;
                        handled = true;
                        break;
                    }
            }
        }

        if (!handled)
            base.WndProc(ref m);
    }

Based on Charles P. solution made some modifications to it, hope it helps others too :) Small checks and improvements to not declare extra variables every time the windows message is called. Also checks not painting the grip anchor when windows state is maximized. I wanted to create a custom control out of it, but i ended up filling the form with this code unfortunately.

Constructor or in the designer file:

this.DoubleBuffered = true;
this.ResizeRedraw   = true;

Overriding windows functions:

    const uint WM_NCHITTEST = 0x0084, WM_MOUSEMOVE = 0x0200,
                 HTLEFT = 10, HTRIGHT = 11, HTBOTTOMRIGHT = 17,
                 HTBOTTOM = 15, HTBOTTOMLEFT = 16, HTTOP = 12,
                 HTTOPLEFT = 13, HTTOPRIGHT = 14;
    Size formSize;
    Point screenPoint;
    Point clientPoint;
    Dictionary<uint, Rectangle> boxes;
    const int RHS = 10; // RESIZE_HANDLE_SIZE
    bool handled;

    protected override void WndProc(ref Message m)
    {
        if (this.WindowState == FormWindowState.Maximized)
        {
            base.WndProc(ref m);
            return;
        }

        handled  = false;
        if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
        {
            formSize    = this.Size;
            screenPoint = new Point(m.LParam.ToInt32());
            clientPoint = this.PointToClient(screenPoint);

            boxes = new Dictionary<uint, Rectangle>() {
                {HTBOTTOMLEFT, new Rectangle(0, formSize.Height - RHS, RHS, RHS)},
                {HTBOTTOM, new Rectangle(RHS, formSize.Height - RHS, formSize.Width - 2*RHS, RHS)},
                {HTBOTTOMRIGHT, new Rectangle(formSize.Width - RHS, formSize.Height - RHS, RHS, RHS)},
                {HTRIGHT, new Rectangle(formSize.Width - RHS, RHS, RHS, formSize.Height - 2*RHS)},
                {HTTOPRIGHT, new Rectangle(formSize.Width - RHS, 0, RHS, RHS) },
                {HTTOP, new Rectangle(RHS, 0, formSize.Width - 2*RHS, RHS) },
                {HTTOPLEFT, new Rectangle(0, 0, RHS, RHS) },
                {HTLEFT, new Rectangle(0, RHS, RHS, formSize.Height - 2*RHS) }
            };

            foreach (var hitBox in boxes)
            {
                if (hitBox.Value.Contains(clientPoint))
                {
                    m.Result = (IntPtr)hitBox.Key;
                    handled  = true;
                    break;
                }
            }
        }

        if (!handled)
            base.WndProc(ref m);
    }


    protected override void OnPaint(PaintEventArgs e)
    {
        if (this.WindowState != FormWindowState.Maximized)
        {
            ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor,
                this.ClientSize.Width - 16, this.ClientSize.Height - 16, 16, 16);
        }

        base.OnPaint(e);
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!