如何实现点击外部按钮后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 } } }
解决方案
通过添加一个可外部控制的属性,修改消息处理逻辑即可实现需求:
- 添加
AllowDrag公共属性,用于控制是否允许拖动 - 在
WM_NCHITTEST中,当不允许拖动时,将控件内部区域的命中测试结果改为Client(值为1),避免触发拖动行为 - 在
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
相关产品推荐
相关产品推荐

