简体   繁体   English

如何使用表单边框上的控件修复无边框表单调整大小?

[英]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:我有一个需要调整大小的无边界 winForm,我设法这样做:

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 + "\n");
            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 + "\n");
        }
        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:下面是一个例子:

调整大小问题

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?有没有办法解决这个问题?

The problem here is that it is the Label control that gets the mouse notifications, not your borderless form.这里的问题是获取鼠标通知的是 Label 控件,而不是您的无边框表单。 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.您已经知道如何做到这一点,WM_NCHITTEST 还允许返回 HTTRANSPARENT。 Windows keeps looking for the next candidate for the notification, it will be the label's Parent. Windows 一直在寻找通知的下一个候选者,它将是标签的父级。

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.适用于任何 Control 类,如果它是一个按钮,您会希望更具选择性。 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.您可以使用的另一种技术在本机 Windows 编程中称为“子类化”。 Universally used in Winforms to create wrapper .NET classes for native Windows controls.在 Winforms 中普遍用于为本机 Windows 控件创建包装器 .NET 类。 It works well here too, you can have a peek at the messages of any control and intercept WM_NCHITTEST that way:它在这里也很有效,您可以查看任何控件的消息并以这种方式拦截 WM_NCHITTEST:

    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:只需为靠近窗口边缘的每个控件创建一个 MouseFilter 实例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM