【轉】C# 实现拖拉控件改变位置与大小

本文介绍了如何在运行时动态改变控件的位置与大小,实现类似Visual Studio设计界面的拖拉功能。通过创建MoveControl类并挂载事件,实现了在拖动控件时实时调整其Location与Size,同时还提供了绘制边框的功能以辅助定位。通过实例演示了如何在Form窗体中应用此功能,并展示了如何测试与优化拖动效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文網址:http://www.cnblogs.com/wangshenhe/archive/2012/05/14/2499159.html

前言:

  很多时候我们需要在运行时,动态地改变控件的位置以及大小,以获得更好的布局。比如说实际项目中的可自定义的报表、可自定义的单据等诸如此类。它们有个特点就是允许客户或者二次开发人员设计它们需要的界面设置功能。

  本人以前也做过可自定义系统,包括界面和功能,主要为了减少开发人员的工作量以及程序的灵活性和健壮性。

  本篇主要讨论下,在运行时如何实现拖拉控件,达到改变控件位置与大小。功能将模拟VS设计界面时的拖拉功能。

  (本篇暂不涉及多控件同时操作)

 

 

一、技术概述

  其实实现运行时控件的拖拉并不难,主要是改变控件的Location与Size即可。动态调整时再捕获MouseDown、MouseMove及MouseUp事件来实时修改上述两个属性就可以实现。

 

二、功能规划

  在此之前,我们先来看下.net设计界面,一旦选中某个控件时,将会出现如下图的边框:

  

  之后就可以通过拖拉出现的边框改变其大小。而改变控件的位置,实际上是当鼠标点击在控件内部拖动时实现的。

  所有本例也将功能分为两个部分实现,分别为控件内部拖动改变位置与控件边框拖拉改变大小。

 

三、具体实现

1.拖动控件改变位置

  首先,新建一个项目,然后添加一个类,取名叫MoveControl,该类用来给控件挂载事件实现拖动。

  接着在该类中添加字段currentControl,用来保存需要操作的控件,即通过构造函数传递的控件。

  接着创建一方法--AddEvents,用来给当前的控件挂载事件。

  代码如下:   

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4 using System.Windows.Forms;
 5 using System.Drawing;
 6 
 7 namespace DragControl
 8 {
 9     public class MoveControl
10     {
11         #region Constructors
12         public MoveControl(Control ctrl)
13         {
14             currentControl = ctrl;
15             AddEvents();
16         }
17         #endregion
18 
19         #region Fields
20         private Control currentControl; //传入的控件
21         #endregion
22 
23         #region Properties
24 
25         #endregion
26 
27         #region Methods
28         /// <summary>
29         /// 挂载事件
30         /// </summary>
31         private void AddEvents()
32         {
33             currentControl.MouseClick += new MouseEventHandler(MouseClick);
34             currentControl.MouseDown += new MouseEventHandler(MouseDown);
35             currentControl.MouseMove += new MouseEventHandler(MouseMove);
36             currentControl.MouseUp += new MouseEventHandler(MouseUp);
37         }
38         #endregion
39 
40         #region Events
41         /// <summary>
42         /// 鼠标单击事件:用来显示边框
43         /// </summary>
44         /// <param name="sender"></param>
45         /// <param name="e"></param>
46         void MouseClick(object sender, MouseEventArgs e)
47         {
48         }
49 
50         /// <summary>
51         /// 鼠标按下事件:记录当前鼠标相对窗体的坐标
52         /// </summary>
53         void MouseDown(object sender, MouseEventArgs e)
54         {
55 
56         }
57 
58         /// <summary>
59         /// 鼠标移动事件:让控件跟着鼠标移动
60         /// </summary>
61         void MouseMove(object sender, MouseEventArgs e)
62         {
63         }
64 
65         /// <summary>
66         /// 鼠标弹起事件:让自定义的边框出现
67         /// </summary>
68         void MouseUp(object sender, MouseEventArgs e)
69         {
70         }
71         #endregion
72     }
73 }
复制代码

  接着我们需要实现MouseDown、MouseMove、MouseUp三个事件。

  不过在此之前,我们必须要弄清楚,移动即表示坐标的改变,所以必定要有个起始坐标和终点坐标。

  所以我们在MoveControl类中加入两个字段。

private Point pPoint; //上个鼠标坐标
private Point cPoint; //当前鼠标坐标

  而且在开始拖动之前,我们肯定需要先单击一次控件。在MouseDown时获取当前光标的位置,保存到pPoint中。

  (此处用Cursor获得坐标的好处,就是忽略掉容器的麻烦问题)

复制代码
1         /// <summary>
2         /// 鼠标单击事件:用来显示边框
3         /// </summary>
4         void MouseClick(object sender, MouseEventArgs e)
5         {
6             pPoint = Cursor.Position;   
7         }
复制代码

  接着便实现MouseMove的事件,当鼠标左键按下时,接着移动鼠标后,继续鼠标移动后的坐标,然后与MouseDown时记下的坐标相减,就得到鼠标的位移值,接着控件的Location加上该位移值即可,然后更新pPoint。  

复制代码
 1         /// <summary>
 2         /// 鼠标移动事件:让控件跟着鼠标移动
 3         /// </summary>
 4         void MouseMove(object sender, MouseEventArgs e)
 5         {
 6             Cursor.Current = Cursors.SizeAll; //当鼠标处于控件内部时,显示光标样式为SizeAll
 7             //当鼠标左键按下时才触发
 8             if (e.Button == MouseButtons.Left)
 9             {
10                 cPoint = Cursor.Position; //获得当前鼠标位置
11                 int x = cPoint.X - pPoint.X;
12                 int y = cPoint.Y - pPoint.Y;
13                 currentControl.Location = new Point(currentControl.Location.X + x, currentControl.Location.Y + y);
14                 pPoint = cPoint;
15             }
16         }
复制代码

  由于此时还没涉及到边框,所以MouseUp暂时不用处理。至此拖动的基本功能已经实现!

  目前MoveControl的完整代码如下:  

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Text;
 4 using System.Windows.Forms;
 5 using System.Drawing;
 6 
 7 namespace DragControl
 8 {
 9     public class MoveControl
10     {
11         #region Constructors
12         public MoveControl(Control ctrl)
13         {
14             currentControl = ctrl;
15             AddEvents();
16         }
17         #endregion
18 
19         #region Fields
20         private Control currentControl; //传入的控件
21         private Point pPoint; //上个鼠标坐标
22         private Point cPoint; //当前鼠标坐标
23         #endregion
24 
25         #region Properties
26 
27         #endregion
28 
29         #region Methods
30         /// <summary>
31         /// 挂载事件
32         /// </summary>
33         private void AddEvents()
34         {
35             currentControl.MouseDown += new MouseEventHandler(MouseDown);
36             currentControl.MouseMove += new MouseEventHandler(MouseMove);
37             currentControl.MouseUp += new MouseEventHandler(MouseUp);
38         }
39 
40         /// <summary>
41         /// 绘制拖拉时的黑色边框
42         /// </summary>
43         public static void DrawDragBound(Control ctrl)
44         {
45             ctrl.Refresh();
46             Graphics g = ctrl.CreateGraphics();
47             int width = ctrl.Width;
48             int height = ctrl.Height;
49             Point[] ps = new Point[5]{new Point(0,0),new Point(width -1,0),
50                 new Point(width -1,height -1),new Point(0,height-1),new Point(0,0)};
51             g.DrawLines(new Pen(Color.Black), ps);
52         }   
53         #endregion
54 
55         #region Events
56         /// <summary>
57         /// 鼠标按下事件:记录当前鼠标相对窗体的坐标
58         /// </summary>
59         void MouseDown(object sender, MouseEventArgs e)
60         {
61             pPoint = Cursor.Position;
62         }
63 
64         /// <summary>
65         /// 鼠标移动事件:让控件跟着鼠标移动
66         /// </summary>
67         void MouseMove(object sender, MouseEventArgs e)
68         {
69             Cursor.Current = Cursors.SizeAll; //当鼠标处于控件内部时,显示光标样式为SizeAll
70             //当鼠标左键按下时才触发
71             if (e.Button == MouseButtons.Left)
72             {
73                 MoveControl.DrawDragBound(this.currentControl);
74                 cPoint = Cursor.Position; //获得当前鼠标位置
75                 int x = cPoint.X - pPoint.X;
76                 int y = cPoint.Y - pPoint.Y;
77                 currentControl.Location = new Point(currentControl.Location.X + x, currentControl.Location.Y + y);
78                 pPoint = cPoint;
79             }
80         }
81 
82         /// <summary>
83         /// 鼠标弹起事件:让自定义的边框出现
84         /// </summary>
85         void MouseUp(object sender, MouseEventArgs e)
86         {
87             this.currentControl.Refresh();
88         }
89         #endregion
90     }
91 }
复制代码

 

 

  下面我们来测试下拖动的功能。

  创建一个Form窗体,可以再界面上添加你要测试的控件类型,此处我只用TextBox左下测试。在Load的中添加以下代码,将Form中的所有控件挂载上拖拉功能。

复制代码
1         private void Form1_Load(object sender, EventArgs e)
2         {
3             foreach (Control ctrl in this.Controls)
4             {
5                 new MoveControl(ctrl);
6             }
7         }
复制代码

  

  此时,有心人可能会发现VS中拖动控件时,将会出现黑色边框,而处于没有。

  这也很简单,我们在MouseMove时加上如下代码即可。

复制代码
 1         /// <summary>
 2         /// 绘制拖拉时的黑色边框
 3         /// </summary>
 4         public static void DrawDragBound(Control ctrl)
 5         {
 6             ctrl.Refresh();
 7             Graphics g = ctrl.CreateGraphics();
 8             int width = ctrl.Width;
 9             int height = ctrl.Height;
10             Point[] ps = new Point[5]{new Point(0,0),new Point(width -1,0),
11                 new Point(width -1,height -1),new Point(0,height-1),new Point(0,0)};
12             g.DrawLines(new Pen(Color.Black), ps);
13         }    
14 
15 
16         /// <summary>
17         /// 鼠标移动事件:让控件跟着鼠标移动
18         /// </summary>
19         void MouseMove(object sender, MouseEventArgs e)
20         {
21             Cursor.Current = Cursors.SizeAll; //当鼠标处于控件内部时,显示光标样式为SizeAll
22             //当鼠标左键按下时才触发
23             if (e.Button == MouseButtons.Left)
24             {
25                 MoveControl.DrawDragBound(this.currentControl);
26                 cPoint = Cursor.Position; //获得当前鼠标位置
27                 int x = cPoint.X - pPoint.X;
28                 int y = cPoint.Y - pPoint.Y;
29                 currentControl.Location = new Point(currentControl.Location.X + x, currentControl.Location.Y + y);
30                 pPoint = cPoint;
31             }
32         }
复制代码

  同时要在MoveUp的时候,刷新一下自己,让黑色边框消失掉!  

复制代码
1         /// <summary>
2         /// 鼠标弹起事件:让自定义的边框出现
3         /// </summary>
4         void MouseUp(object sender, MouseEventArgs e)
5         {
6             this.currentControl.Refresh();
7         }
复制代码

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值