Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
408 views
in Technique[技术] by (71.8m points)

c# - How to fix borderless form resize with controls on borders of the form?

I have a borderless winForm which I needed to resize, and I managed to do it this way:

protected override void WndProc(ref Message m)
    {
        const int wmNcHitTest = 0x84;
        const int htLeft = 10;
        const int htRight = 11;
        const int htTop = 12;
        const int htTopLeft = 13;
        const int htTopRight = 14;
        const int htBottom = 15;
        const int htBottomLeft = 16;
        const int htBottomRight = 17;

        if (m.Msg == wmNcHitTest)
        {
            Console.Write(true + "
");
            int x = (int)(m.LParam.ToInt64() & 0xFFFF);
            int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
            Point pt = PointToClient(new Point(x, y));
            Size clientSize = ClientSize;
            ///allow resize on the lower right corner
            if (pt.X >= clientSize.Width - 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
                return;
            }
            ///allow resize on the lower left corner
            if (pt.X <= 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(IsMirrored ? htBottomRight : htBottomLeft);
                return;
            }
            ///allow resize on the upper right corner
            if (pt.X <= 16 && pt.Y <= 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(IsMirrored ? htTopRight : htTopLeft);
                return;
            }
            ///allow resize on the upper left corner
            if (pt.X >= clientSize.Width - 16 && pt.Y <= 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(IsMirrored ? htTopLeft : htTopRight);
                return;
            }
            ///allow resize on the top border
            if (pt.Y <= 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(htTop);
                return;
            }
            ///allow resize on the bottom border
            if (pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(htBottom);
                return;
            }
            ///allow resize on the left border
            if (pt.X <= 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(htLeft);
                return;
            }
            ///allow resize on the right border
            if (pt.X >= clientSize.Width - 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(htRight);
                return;
            }
        }
        else
        {
            Console.Write(false + "
");
        }
        base.WndProc(ref m);
    }

The problem is that there are controls on the left and right borders of my form, so the resize override used on the code above doesn't work on those areas in which there are controls of any kind.

Here is an example:

Resize problem

On the image above you can see that the label inside the marked area is on the left border of my form and it won't let me resize it.

Is there a way to solve this issue?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem here is that it is the Label control that gets the mouse notifications, not your borderless form. By far the best way to solve this problem is making the label transparent to the mouse. You already know how to do that, WM_NCHITTEST also permits returning HTTRANSPARENT. Windows keeps looking for the next candidate for the notification, it will be the label's Parent.

Especially easy to do for a label since you don't normally have any use for its mouse events at all:

using System;
using System.Windows.Forms;

public class LabelEx : Label {
    protected override void WndProc(ref Message m) {
        const int wmNcHitTest = 0x84;
        const int htTransparent = -1;
        if (!DesignMode && m.Msg == wmNcHitTest) m.Result = new IntPtr(htTransparent);
        else base.WndProc(ref m);
    }
}

Works for any Control class, you'd want to be more selective if it were a button. Might be all you need, still pretty awkward however if you have a lot of different kind of controls close to the edge. Another technique you can use is called "sub-classing" in native Windows programming. Universally used in Winforms to create wrapper .NET classes for native Windows controls. It works well here too, you can have a peek at the messages of any control and intercept WM_NCHITTEST that way:

    const int edge = 16;

    class MouseFilter : NativeWindow {
        private Form form;
        public MouseFilter(Form form, Control child) {
            this.form = form;
            this.AssignHandle(child.Handle);
        }
        protected override void WndProc(ref Message m) {
            const int wmNcHitTest = 0x84;
            const int htTransparent = -1;

            if (m.Msg == wmNcHitTest) {
                var pos = new Point(m.LParam.ToInt32());
                if (pos.X < this.form.Left + edge ||
                    pos.Y < this.form.Top + edge||
                    pos.X > this.form.Right - edge ||
                    pos.Y > this.form.Bottom - edge) {
                    m.Result = new IntPtr(htTransparent);
                    return;
                }
            }
            base.WndProc(ref m);
        }
    }

And just create a MouseFilter instance for every control that gets close to the window edge:

    protected override void OnLoad(EventArgs e) {
        base.OnLoad(e);
        subClassChildren(this.Controls);
    }

    private void subClassChildren(Control.ControlCollection ctls) {
        foreach (Control ctl in ctls) {
            var rc = this.RectangleToClient(this.RectangleToScreen(ctl.DisplayRectangle));
            if (rc.Left < edge || rc.Right > this.ClientSize.Width - edge ||
                rc.Top < edge || rc.Bottom > this.ClientSize.Height - edge) {
                new MouseFilter(this, ctl);
            }
            subClassChildren(ctl.Controls);
        }
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...