You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现点击外部按钮后FrameControl禁止拖动仅保留调整大小功能?

禁用FrameControl拖动但保留调整大小功能的实现方案

问题描述

我正在开发截图功能,允许用户选择捕获区域,基于示例实现了覆盖在PictureBox上的FrameControl控件,目前控件运行正常,但需要实现以下需求:点击控件外的按钮后,控件不再可拖动,光标从SizeAll切换为Arrow,但保留调整大小的功能。

原控件代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ControlSizable
{
    public partial class FrameControl: UserControl
    {
       public FrameControl()
       {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        DoubleBuffered = true;
        ResizeRedraw = true;
        BackColor = Color.Transparent;
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        using (var p = new Pen(Color.White , 3))
        {
            p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
            e.Graphics.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
        }
    }
    const int WM_NCHITTEST = 0x84;
    const int WM_SETCURSOR = 0x20;
    const int WM_NCLBUTTONDBLCLK = 0xA3;
    protected override void WndProc(ref Message m)
    {
        int borderWidth = 10;
        if (m.Msg == WM_SETCURSOR)  /*Setting cursor to SizeAll*/
        {
            if ((m.LParam.ToInt32() & 0xffff) == 0x2 /*Move*/)
            {
                Cursor.Current = Cursors.SizeAll;
                m.Result = (IntPtr)1;
                return;
            }
        }
        if ((m.Msg == WM_NCLBUTTONDBLCLK)) /*Disable Mazimiz on Double click*/
        {
            m.Result = (IntPtr)1;
            return;
        }
        base.WndProc(ref m);
        if (m.Msg == WM_NCHITTEST)
        {
            var pos = PointToClient(new Point(m.LParam.ToInt32() & 0xffff,
                m.LParam.ToInt32() >> 16));
            if (pos.X <= ClientRectangle.Left + borderWidth &&
                pos.Y <= ClientRectangle.Top + borderWidth)
                m.Result = new IntPtr(13); //TOPLEFT
            else if (pos.X >= ClientRectangle.Right - borderWidth &&
                pos.Y <= ClientRectangle.Top + borderWidth)
                m.Result = new IntPtr(14); //TOPRIGHT
            else if (pos.X <= ClientRectangle.Left + borderWidth &&
                pos.Y >= ClientRectangle.Bottom - borderWidth)
                m.Result = new IntPtr(16); //BOTTOMLEFT
            else if (pos.X >= ClientRectangle.Right - borderWidth &&
                pos.Y >= ClientRectangle.Bottom - borderWidth)
                m.Result = new IntPtr(17); //BOTTOMRIGHT
            else if (pos.X <= ClientRectangle.Left + borderWidth)
                m.Result = new IntPtr(10); //LEFT
            else if (pos.Y <= ClientRectangle.Top + borderWidth)
                m.Result = new IntPtr(12); //TOP
            else if (pos.X >= ClientRectangle.Right - borderWidth)
                m.Result = new IntPtr(11); //RIGHT
            else if (pos.Y >= ClientRectangle.Bottom - borderWidth)
                m.Result = new IntPtr(15); //Bottom
            else
                m.Result = new IntPtr(2); //Move
        }
    }
}

解决方案

通过添加一个可外部控制的属性,修改消息处理逻辑即可实现需求:

  1. 添加AllowDrag公共属性,用于控制是否允许拖动
  2. 在WM_NCHITTEST中,当不允许拖动时,将控件内部区域的命中测试结果改为Client(值为1),避免触发拖动行为
  3. 在WM_SETCURSOR中,根据AllowDrag属性决定光标样式,不允许拖动时显示默认Arrow光标

修改后的完整代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ControlSizable
{
    public partial class FrameControl: UserControl
    {
        // 添加公共属性控制是否允许拖动
        [DefaultValue(true)]
        public bool AllowDrag { get; set; } = true;

        public FrameControl()
        {
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            DoubleBuffered = true;
            ResizeRedraw = true;
            BackColor = Color.Transparent;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            using (var p = new Pen(Color.White, 3))
            {
                p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                e.Graphics.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
            }
        }

        const int WM_NCHITTEST = 0x84;
        const int WM_SETCURSOR = 0x20;
        const int WM_NCLBUTTONDBLCLK = 0xA3;
        const int HTCLIENT = 1; // 客户端区域,不会触发拖动
        const int HTMOVE = 2;   // 可拖动区域

        protected override void WndProc(ref Message m)
        {
            int borderWidth = 10;

            if (m.Msg == WM_SETCURSOR)
            {
                if ((m.LParam.ToInt32() & 0xffff) == HTMOVE && AllowDrag)
                {
                    Cursor.Current = Cursors.SizeAll;
                    m.Result = (IntPtr)1;
                    return;
                }
                // 不允许拖动时,使用默认Arrow光标
            }

            if (m.Msg == WM_NCLBUTTONDBLCLK)
            {
                m.Result = (IntPtr)1;
                return;
            }

            base.WndProc(ref m);

            if (m.Msg == WM_NCHITTEST)
            {
                var pos = PointToClient(new Point(m.LParam.ToInt32() & 0xffff,
                    m.LParam.ToInt32() >> 16));

                if (pos.X <= ClientRectangle.Left + borderWidth &&
                    pos.Y <= ClientRectangle.Top + borderWidth)
                    m.Result = new IntPtr(13); //TOPLEFT
                else if (pos.X >= ClientRectangle.Right - borderWidth &&
                    pos.Y <= ClientRectangle.Top + borderWidth)
                    m.Result = new IntPtr(14); //TOPRIGHT
                else if (pos.X <= ClientRectangle.Left + borderWidth &&
                    pos.Y >= ClientRectangle.Bottom - borderWidth)
                    m.Result = new IntPtr(16); //BOTTOMLEFT
                else if (pos.X >= ClientRectangle.Right - borderWidth &&
                    pos.Y >= ClientRectangle.Bottom - borderWidth)
                    m.Result = new IntPtr(17); //BOTTOMRIGHT
                else if (pos.X <= ClientRectangle.Left + borderWidth)
                    m.Result = new IntPtr(10); //LEFT
                else if (pos.Y <= ClientRectangle.Top + borderWidth)
                    m.Result = new IntPtr(12); //TOP
                else if (pos.X >= ClientRectangle.Right - borderWidth)
                    m.Result = new IntPtr(11); //RIGHT
                else if (pos.Y >= ClientRectangle.Bottom - borderWidth)
                    m.Result = new IntPtr(15); //Bottom
                else
                    // 根据AllowDrag属性决定是否允许拖动
                    m.Result = AllowDrag ? new IntPtr(HTMOVE) : new IntPtr(HTCLIENT);
            }
        }
    }
}

使用方式

在外部按钮的点击事件中,直接设置控件的AllowDrag属性即可:

// 假设frameControl是你的控件实例
private void btnDisableDrag_Click(object sender, EventArgs e)
{
    frameControl.AllowDrag = false;
}

控件使用示例

控件使用示例截图

内容的提问来源于stack exchange,提问作者Luiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 13:44:55