C#基础系列:开发自己的窗体设计器(在容器上拖动鼠标增加控件)
2013-01-11 17:19
846 查看
【转 /article/1778186.html】
本文我们实现一个简单的通过拖动鼠标来增加控件的方法。
在我们使用vs2005来开发WinForm应用程序的时候,我们通过选择左边“工具箱”中的某个控件,然后在我们自己的窗体上通过拖动鼠标,一个我们需要的控件就出现了,觉得很爽!其实vs2005中已经有这方面的组件,我们通过简单的代码就可以直接使用,这个你可以用“窗体设计器”Google一下,已经有人做了这方面的介绍。
所以我要说的是,难道除了使用vs2005中提供的这个标准组件,就不能自己弄出一个来?即使不够强大,但是最终都是自己折腾出来的,所以也会很爽。下面我就这个功能点做一个介绍,实现效果图如下:
实现关键点:
1、动态加载控件:
在vs2005中,在某个容器控件上增加新的子控件相当的简单,语法如下,如,增加一个TextBox:
Control.Controls.Add(new TextBox());
2、选择要添加的控件:
通过一个全局的单件类来实现。
3、捕获窗体(或者容器控件,比如Panel,GroupBox等)上鼠标的移动区域,并绘制选择框,如上图中黑色的框。
对于1,是很容易实现的,所以不是我们讨论的重点,我主要讨论2和3点。
第2点:选择需要添加的控件,为了尽量的使Winform中的代码简洁,以及和其它的部分解耦,我们尽量不要考虑在WinForm中使用全局变量或者与其它模块通信的接口。所以我们使用一个单件类SettingService来作为Winform和其它部分通信的中介。
public class SettingService
{
private static SettingService _Instance;
private System.Windows.Forms.Control _SelectedToolBoxControl;
/// <summary>
/// 在Form中,选择了某个要添加的控件后,这里保存这个控件的一个新实例
/// </summary>
public System.Windows.Forms.Control SelectedToolBoxControl
{
get { return this._SelectedToolBoxControl; }
set { this._SelectedToolBoxControl = value; }
}
private SettingService()
{ }
/// <summary>
/// 这里使用单件
/// </summary>
public static SettingService Instance
{
get
{
if (_Instance == null)
{
_Instance = new SettingService();
}
return _Instance;
}
}
}
第3点:因为这里涉及到捕获鼠标的选择区域,并且在按照这个区域的起始以及大小绘制控件。要捕获鼠标,我们需要实现容器控件(Winform也是一个Control,所以这里称的容器控件包括WinForm)的Control_ MouseDown,Control_ MouseMove,Control_ MouseUp,Control_ MouseEnter这几个事件。
代码如下:
public class MouseHook
{
Control _Owner;
private int _CLickAtX;
private int _ClickAtY;
private int _MoveAtX;
private int _MoveAtY;
private bool _BeginDrag;
private bool _BeginDrawControl;
/// <summary>
/// 这里Owner使用的是Control类型,是因为我们不仅仅需要在Winform上增加控件,
/// 也需要在其它容器,比如Panel,GroupBox等上面增加容器
/// </summary>
/// <param name="Owner"></param>
public MouseHook(System.Windows.Forms.Control Owner)
{
this._Owner = Owner;
this._Owner.MouseDown += new MouseEventHandler(this.Control_MouseDown);
this._Owner.MouseMove += new MouseEventHandler(this.Control_MouseMove);
this._Owner.MouseUp += new MouseEventHandler(this.Control_MouseUp);
this._Owner.MouseEnter += new EventHandler(this.Control_MouseEnter);
this._BeginDrawControl = false;
}
#region Control上的鼠标事件
void Control_MouseDown(object sender, MouseEventArgs e)
{
//如果没有选择控件,那么退出
if (SettingService.Instance.SelectedToolBoxControl == null)
{
return;
}
this._CLickAtX = e.X;
this._ClickAtY = e.Y;
this._MoveAtX = e.X;
this._MoveAtY = e.Y;
this._BeginDrag = true;
if (SettingService.Instance.SelectedToolBoxControl != null)
{
this._BeginDrawControl = true;
}
else
{
this._BeginDrawControl = false;
}
}
void Control_MouseMove(object sender, MouseEventArgs e)
{
if (SettingService.Instance.SelectedToolBoxControl == null)
{
return;
}
if (this._BeginDrag)
{
//取消上次绘制的选择框
int iLeft, iTop, iWidth, iHeight;
Pen pen;
Rectangle rect;
pen = new Pen(this._Owner.BackColor);
if (this._BeginDrawControl == true)
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
pen.Width = 2;
}
else
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
}
iLeft = this._CLickAtX < this._MoveAtX ? this._CLickAtX : this._MoveAtX;
iTop = this._ClickAtY < this._MoveAtY ? this._ClickAtY : this._MoveAtY;
iWidth = Math.Abs(this._MoveAtX - this._CLickAtX);
iHeight = Math.Abs(this._MoveAtY - this._ClickAtY);
rect = new Rectangle(iLeft, iTop, iWidth, iHeight);
this._Owner.CreateGraphics().DrawRectangle(pen, rect);
//重新绘制选择框
this._MoveAtX = e.X;
this._MoveAtY = e.Y;
pen = new Pen(Color.Black);
if (this._BeginDrawControl == true)
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
pen.Width = 2;
}
else
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
}
iLeft = this._CLickAtX < this._MoveAtX ? this._CLickAtX : this._MoveAtX;
iTop = this._ClickAtY < this._MoveAtY ? this._ClickAtY : this._MoveAtY;
iWidth = Math.Abs(this._MoveAtX - this._CLickAtX);
iHeight = Math.Abs(this._MoveAtY - this._ClickAtY);
rect = new Rectangle(iLeft, iTop, iWidth, iHeight);
this._Owner.CreateGraphics().DrawRectangle(pen, rect);
}
}
void Control_MouseUp(object sender, MouseEventArgs e)
{
this._BeginDrag = false;
this._Owner.SuspendLayout();
if (SettingService.Instance.SelectedToolBoxControl == null)
{
return;
}
//取消上次绘制的选择框
int iLeft, iTop, iWidth, iHeight;
Pen pen;
Rectangle rect;
pen = new Pen(this._Owner.BackColor);
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
iLeft = this._CLickAtX < this._MoveAtX ? this._CLickAtX : this._MoveAtX;
iTop = this._ClickAtY < this._MoveAtY ? this._ClickAtY : this._MoveAtY;
iWidth = Math.Abs(this._MoveAtX - this._CLickAtX);
iHeight = Math.Abs(this._MoveAtY - this._ClickAtY);
rect = new Rectangle(iLeft, iTop, iWidth, iHeight);
this._Owner.CreateGraphics().DrawRectangle(pen, rect);
if (SettingService.Instance.SelectedToolBoxControl != null)
{
AddControl(SettingService.Instance.SelectedToolBoxControl, rect);
}
else
{
//这里是拖动鼠标,选择控件,这里将会在后续的介绍中给出
}
this._Owner.Refresh();
this._Owner.ResumeLayout();
}
void Control_MouseEnter(object sender, EventArgs e)
{
if (SettingService.Instance.SelectedToolBoxControl != null)
{
this._Owner.Cursor = Cursors.Cross;
}
else
{
this._Owner.Cursor = Cursors.Default;
}
}
private void AddControl(System.Windows.Forms.Control control, Rectangle rect)
{
try
{
control.Location = rect.Location;
control.Size = rect.Size;
control.Name = GetControlName(control);
//因为对于DataTimePiker控件来说不能设置.Text为非日期型,所以忽略错误
try
{
control.Text = GetControlType(control);
}
catch { }
this._Owner.Controls.Add(control);
control.Visible = true;
this._Owner.Cursor = Cursors.Default;
SettingService.Instance.SelectedToolBoxControl = null;
}
catch (Exception e)
{
this._Owner.Cursor = Cursors.Default;
SettingService.Instance.SelectedToolBoxControl = null;
}
}
private string GetControlType(System.Windows.Forms.Control ctrl)
{
string strType = ctrl.GetType().ToString();
string strControlType;
string[] strArr = strType.Split(".".ToCharArray());
strControlType = strArr[strArr.Length - 1].Trim();
return strControlType;
}
private string GetControlName(System.Windows.Forms.Control control)
{
//这里简单返回控件名,如果需要,可以通过修改这个函数做特殊处理
return control.GetType().Name;
}
#endregion
}
另外Form代码如下,这里为了方便,我直接先使用几个Button来替代实际中的ToolBox:
//在Form中增加几个Button,分别命名为cmdArrow,cmdLabel,cmdTextBox,cmdComboBox,cmdGroupBox
public partial class Form1 : Form
{
private MouseHook _MouseHook;
public Form1()
{
InitializeComponent();
this._MouseHook = new MouseHook(this);
}
private void cmdArrow_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = null;
}
private void cmdLabel_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = new Label();
}
private void cmdTextBox_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = new TextBox();
}
private void cmdComboBox_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = new ComboBox();
}
private void cmdGroupBox_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = new GroupBox();
}
}
上面就是简单实现拖动增加控件的方法,如果要在GroupBox中增加控件的话,只需要再new 一个MouseHook,如:new MouseHook(GroupBoxControl)这样就可以了的。
本文我们实现一个简单的通过拖动鼠标来增加控件的方法。
在我们使用vs2005来开发WinForm应用程序的时候,我们通过选择左边“工具箱”中的某个控件,然后在我们自己的窗体上通过拖动鼠标,一个我们需要的控件就出现了,觉得很爽!其实vs2005中已经有这方面的组件,我们通过简单的代码就可以直接使用,这个你可以用“窗体设计器”Google一下,已经有人做了这方面的介绍。
所以我要说的是,难道除了使用vs2005中提供的这个标准组件,就不能自己弄出一个来?即使不够强大,但是最终都是自己折腾出来的,所以也会很爽。下面我就这个功能点做一个介绍,实现效果图如下:
实现关键点:
1、动态加载控件:
在vs2005中,在某个容器控件上增加新的子控件相当的简单,语法如下,如,增加一个TextBox:
Control.Controls.Add(new TextBox());
2、选择要添加的控件:
通过一个全局的单件类来实现。
3、捕获窗体(或者容器控件,比如Panel,GroupBox等)上鼠标的移动区域,并绘制选择框,如上图中黑色的框。
对于1,是很容易实现的,所以不是我们讨论的重点,我主要讨论2和3点。
第2点:选择需要添加的控件,为了尽量的使Winform中的代码简洁,以及和其它的部分解耦,我们尽量不要考虑在WinForm中使用全局变量或者与其它模块通信的接口。所以我们使用一个单件类SettingService来作为Winform和其它部分通信的中介。
public class SettingService
{
private static SettingService _Instance;
private System.Windows.Forms.Control _SelectedToolBoxControl;
/// <summary>
/// 在Form中,选择了某个要添加的控件后,这里保存这个控件的一个新实例
/// </summary>
public System.Windows.Forms.Control SelectedToolBoxControl
{
get { return this._SelectedToolBoxControl; }
set { this._SelectedToolBoxControl = value; }
}
private SettingService()
{ }
/// <summary>
/// 这里使用单件
/// </summary>
public static SettingService Instance
{
get
{
if (_Instance == null)
{
_Instance = new SettingService();
}
return _Instance;
}
}
}
第3点:因为这里涉及到捕获鼠标的选择区域,并且在按照这个区域的起始以及大小绘制控件。要捕获鼠标,我们需要实现容器控件(Winform也是一个Control,所以这里称的容器控件包括WinForm)的Control_ MouseDown,Control_ MouseMove,Control_ MouseUp,Control_ MouseEnter这几个事件。
代码如下:
public class MouseHook
{
Control _Owner;
private int _CLickAtX;
private int _ClickAtY;
private int _MoveAtX;
private int _MoveAtY;
private bool _BeginDrag;
private bool _BeginDrawControl;
/// <summary>
/// 这里Owner使用的是Control类型,是因为我们不仅仅需要在Winform上增加控件,
/// 也需要在其它容器,比如Panel,GroupBox等上面增加容器
/// </summary>
/// <param name="Owner"></param>
public MouseHook(System.Windows.Forms.Control Owner)
{
this._Owner = Owner;
this._Owner.MouseDown += new MouseEventHandler(this.Control_MouseDown);
this._Owner.MouseMove += new MouseEventHandler(this.Control_MouseMove);
this._Owner.MouseUp += new MouseEventHandler(this.Control_MouseUp);
this._Owner.MouseEnter += new EventHandler(this.Control_MouseEnter);
this._BeginDrawControl = false;
}
#region Control上的鼠标事件
void Control_MouseDown(object sender, MouseEventArgs e)
{
//如果没有选择控件,那么退出
if (SettingService.Instance.SelectedToolBoxControl == null)
{
return;
}
this._CLickAtX = e.X;
this._ClickAtY = e.Y;
this._MoveAtX = e.X;
this._MoveAtY = e.Y;
this._BeginDrag = true;
if (SettingService.Instance.SelectedToolBoxControl != null)
{
this._BeginDrawControl = true;
}
else
{
this._BeginDrawControl = false;
}
}
void Control_MouseMove(object sender, MouseEventArgs e)
{
if (SettingService.Instance.SelectedToolBoxControl == null)
{
return;
}
if (this._BeginDrag)
{
//取消上次绘制的选择框
int iLeft, iTop, iWidth, iHeight;
Pen pen;
Rectangle rect;
pen = new Pen(this._Owner.BackColor);
if (this._BeginDrawControl == true)
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
pen.Width = 2;
}
else
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
}
iLeft = this._CLickAtX < this._MoveAtX ? this._CLickAtX : this._MoveAtX;
iTop = this._ClickAtY < this._MoveAtY ? this._ClickAtY : this._MoveAtY;
iWidth = Math.Abs(this._MoveAtX - this._CLickAtX);
iHeight = Math.Abs(this._MoveAtY - this._ClickAtY);
rect = new Rectangle(iLeft, iTop, iWidth, iHeight);
this._Owner.CreateGraphics().DrawRectangle(pen, rect);
//重新绘制选择框
this._MoveAtX = e.X;
this._MoveAtY = e.Y;
pen = new Pen(Color.Black);
if (this._BeginDrawControl == true)
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
pen.Width = 2;
}
else
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
}
iLeft = this._CLickAtX < this._MoveAtX ? this._CLickAtX : this._MoveAtX;
iTop = this._ClickAtY < this._MoveAtY ? this._ClickAtY : this._MoveAtY;
iWidth = Math.Abs(this._MoveAtX - this._CLickAtX);
iHeight = Math.Abs(this._MoveAtY - this._ClickAtY);
rect = new Rectangle(iLeft, iTop, iWidth, iHeight);
this._Owner.CreateGraphics().DrawRectangle(pen, rect);
}
}
void Control_MouseUp(object sender, MouseEventArgs e)
{
this._BeginDrag = false;
this._Owner.SuspendLayout();
if (SettingService.Instance.SelectedToolBoxControl == null)
{
return;
}
//取消上次绘制的选择框
int iLeft, iTop, iWidth, iHeight;
Pen pen;
Rectangle rect;
pen = new Pen(this._Owner.BackColor);
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
iLeft = this._CLickAtX < this._MoveAtX ? this._CLickAtX : this._MoveAtX;
iTop = this._ClickAtY < this._MoveAtY ? this._ClickAtY : this._MoveAtY;
iWidth = Math.Abs(this._MoveAtX - this._CLickAtX);
iHeight = Math.Abs(this._MoveAtY - this._ClickAtY);
rect = new Rectangle(iLeft, iTop, iWidth, iHeight);
this._Owner.CreateGraphics().DrawRectangle(pen, rect);
if (SettingService.Instance.SelectedToolBoxControl != null)
{
AddControl(SettingService.Instance.SelectedToolBoxControl, rect);
}
else
{
//这里是拖动鼠标,选择控件,这里将会在后续的介绍中给出
}
this._Owner.Refresh();
this._Owner.ResumeLayout();
}
void Control_MouseEnter(object sender, EventArgs e)
{
if (SettingService.Instance.SelectedToolBoxControl != null)
{
this._Owner.Cursor = Cursors.Cross;
}
else
{
this._Owner.Cursor = Cursors.Default;
}
}
private void AddControl(System.Windows.Forms.Control control, Rectangle rect)
{
try
{
control.Location = rect.Location;
control.Size = rect.Size;
control.Name = GetControlName(control);
//因为对于DataTimePiker控件来说不能设置.Text为非日期型,所以忽略错误
try
{
control.Text = GetControlType(control);
}
catch { }
this._Owner.Controls.Add(control);
control.Visible = true;
this._Owner.Cursor = Cursors.Default;
SettingService.Instance.SelectedToolBoxControl = null;
}
catch (Exception e)
{
this._Owner.Cursor = Cursors.Default;
SettingService.Instance.SelectedToolBoxControl = null;
}
}
private string GetControlType(System.Windows.Forms.Control ctrl)
{
string strType = ctrl.GetType().ToString();
string strControlType;
string[] strArr = strType.Split(".".ToCharArray());
strControlType = strArr[strArr.Length - 1].Trim();
return strControlType;
}
private string GetControlName(System.Windows.Forms.Control control)
{
//这里简单返回控件名,如果需要,可以通过修改这个函数做特殊处理
return control.GetType().Name;
}
#endregion
}
另外Form代码如下,这里为了方便,我直接先使用几个Button来替代实际中的ToolBox:
//在Form中增加几个Button,分别命名为cmdArrow,cmdLabel,cmdTextBox,cmdComboBox,cmdGroupBox
public partial class Form1 : Form
{
private MouseHook _MouseHook;
public Form1()
{
InitializeComponent();
this._MouseHook = new MouseHook(this);
}
private void cmdArrow_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = null;
}
private void cmdLabel_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = new Label();
}
private void cmdTextBox_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = new TextBox();
}
private void cmdComboBox_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = new ComboBox();
}
private void cmdGroupBox_Click(object sender, EventArgs e)
{
SettingService.Instance.SelectedToolBoxControl = new GroupBox();
}
}
上面就是简单实现拖动增加控件的方法,如果要在GroupBox中增加控件的话,只需要再new 一个MouseHook,如:new MouseHook(GroupBoxControl)这样就可以了的。
相关文章推荐
- C#基础系列:开发自己的窗体设计器(在容器上拖动鼠标增加控件)
- C#基础系列:开发自己的窗体设计器(在容器上拖动鼠标增加控件)
- C#基础系列:开发自己的窗体设计器(实现控件的拖动)
- C#基础系列:开发自己的窗体设计器(实现控件的拖动)
- C#基础系列:开发自己的窗体设计器(实现控件的拖动)
- C#基础系列:开发自己的窗体设计器(实现控件的选择)
- C#基础系列:开发自己的窗体设计器(实现控件的选择)
- C#基础系列:开发自己的窗体设计器(PropertyGrid显示中文属性名)
- C#基础系列:开发自己的窗体设计器(PropertyGrid显示中文属性名)
- C#基础系列:开发自己的窗体设计器(PropertyGrid显示中文属性名)
- 转 C#基础系列:开发自己的窗体设计器(PropertyGrid显示中文属性名)
- C#基础系列:开发自己的窗体设计器(PropertyGrid显示中文属性名)
- C#基础系列:开发自己的窗体设计器(总纲)
- 【Visual Studio风格开发系列 - WinForm设计时异常】关于VS.NET中窗体设计器初始化控件引发的设计时异常
- c# 鼠标点击控件即拖动窗体
- C#WinForm开发系列之CheckBox控件全选和取消全选以及checkedListBox的基础使用介绍
- 【Visual Studio风格开发系列 - WinForm设计时异常】关于VS.NET中窗体设计器初始化控件引发的设计时异常
- c# 鼠标在控件上拖动 移动窗体 移动窗口
- (原创)自已实现服务器控件系列 之 设计时可用鼠标拖动大小的Label控件
- c# 把窗体嵌入到容器控件中