自由调整大小句柄无边框C#

时间:2013-07-19 14:20:47

标签: c# winforms winapi window-resize

我正在尝试制作从工具栏中弹出的无边框表单。我希望用户能够抓住右下角(“调整大小句柄”)并能够调整表单大小,但无法以任何其他方式调整表单大小或重新定位。

我听说我可以拦截发送到表单的WM_NCHITTEST消息并将其结果设置为HTBOTTOMRIGHT,这将让操作系统处理表单的重新调整大小,就好像它有一个相当大的框架。我的想法是检测鼠标指针是否已进入我在角落中定义的框,如果它已经,则返回HTBOTTOMRIGHT结果。

Graphic illustrating the resize handle

这并不像我预期的那样有效。我能够拦截消息,但似乎只有当用户将鼠标光标放在窗体的1px粗边框上时才会发送消息。这意味着如果你非常精确地将光标定位在右下角边缘,它就能按照我想要的方式工作。

这是我的WndProc覆盖:

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

我做错了什么或者有更好的方法来做我想做的事情吗?

非常感谢。

4 个答案:

答案 0 :(得分:14)

我正在寻找类似的东西,安东的代码是一个很好的基础。这就是我最终要从各方面调整工作量。我不确定Dictionary是存储hitbox的最佳方式,但我想这并不重要。

由于我的表单填充了使用Fill作为Dock参数的控件,我只需要为Form添加一个5px填充,以使其正常工作。

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

答案 1 :(得分:2)

对您的代码进行少量修改。我添加了WM_MOUSEMOVE邮件处理:

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

顺便说一下,您可以使用ControlPaint.DrawSizeGrip Method http://msdn.microsoft.com/en-us/library/2e1yx2sa.aspx

绘制系统特定的窗口尺寸。

答案 2 :(得分:2)

Anton Semenov ,我不明白你的代码。

无论如何,我对 Charles P 的第一个代码有疑问 当我最大化窗口,然后尝试改变它的大小 - 它正在调整大小 之后,我无法将其再次修复到正常大小,也无法使用正常的最大按钮再次将其重新设置。

我解决这个问题的方法是在底部的'foreach'循环中添加条件:

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

答案 3 :(得分:1)

基于Charles P.解决方案对它做了一些修改,希望它也能帮助别人:) 小的检查和改进,以便在每次调用Windows消息时都不声明额外的变量。 当窗口状态最大化时,还检查不绘制抓握锚。 我想用它创建一个自定义控件,但不幸的是我最终用这个代码填充了表单。

构造函数或设计器文件中:

<div class="graybgc">
       <span>A: </span>
          <ul >
            <li>123</li>
             <li>123</li>
          </ul>
</div>

覆盖窗口函数:

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