C#基础系列:开发自己的窗体设计器(在容器上拖动鼠标增加控件)

本文我们实现一个简单的通过拖动鼠标来增加控件的方法。

在我们使用vs2005来开发WinForm应用程序的时候,我们通过选择左边“工具箱”中的某个控件,然后在我们自己的窗体上通过拖动鼠标,一个我们需要的控件就出现了,觉得很爽!其实vs2005中已经有这方面的组件,我们通过简单的代码就可以直接使用,这个你可以用“窗体设计器”Google一下,已经有人做了这方面的介绍。

所以我要说的是,难道除了使用vs2005中提供的这个标准组件,就不能自己弄出一个来?即使不够强大,但是最终都是自己折腾出来的,所以也会很爽。下面我就这个功能点做一个介绍,实现效果图如下:

实现关键点:

1、动态加载控件:

vs2005中,在某个容器控件上增加新的子控件相当的简单,语法如下,如,增加一个TextBox

Control.Controls.Add(new TextBox());

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

2、选择要添加的控件:

通过一个全局的单件类来实现。

3、捕获窗体(或者容器控件,比如PanelGroupBox等)上鼠标的移动区域,并绘制选择框,如上图中黑色的框。

对于1,是很容易实现的,所以不是我们讨论的重点,我主要讨论23点。

2点:选择需要添加的控件,为了尽量的使Winform中的代码简洁,以及和其它的部分解耦,我们尽量不要考虑在WinForm中使用全局变量或者与其它模块通信的接口。所以我们使用一个单件类SettingService来作为Winform和其它部分通信的中介。

  1. publicclassSettingService
  2. {
  3. privatestaticSettingService_Instance;
  4. privateSystem.Windows.Forms.Control_SelectedToolBoxControl;
  5. ///<summary>
  6. ///在Form中,选择了某个要添加的控件后,这里保存这个控件的一个新实例
  7. ///</summary>
  8. publicSystem.Windows.Forms.ControlSelectedToolBoxControl
  9. {
  10. get{returnthis._SelectedToolBoxControl;}
  11. set{this._SelectedToolBoxControl=value;}
  12. }
  13. privateSettingService()
  14. {}
  15. ///<summary>
  16. ///这里使用单件
  17. ///</summary>
  18. publicstaticSettingServiceInstance
  19. {
  20. get
  21. {
  22. if(_Instance==null)
  23. {
  24. _Instance=newSettingService();
  25. }
  26. return_Instance;
  27. }
  28. }
  29. }

3点:因为这里涉及到捕获鼠标的选择区域,并且在按照这个区域的起始以及大小绘制控件。要捕获鼠标,我们需要实现容器控件(Winform也是一个Control,所以这里称的容器控件包括WinForm)的Control_ MouseDownControl_ MouseMoveControl_ MouseUpControl_ MouseEnter这几个事件。

代码如下:

  1. publicclassMouseHook
  2. {
  3. Control_Owner;
  4. privateint_CLickAtX;
  5. privateint_ClickAtY;
  6. privateint_MoveAtX;
  7. privateint_MoveAtY;
  8. privatebool_BeginDrag;
  9. privatebool_BeginDrawControl;
  10. ///<summary>
  11. ///这里Owner使用的是Control类型,是因为我们不仅仅需要在Winform上增加控件,
  12. ///也需要在其它容器,比如Panel,GroupBox等上面增加容器
  13. ///</summary>
  14. ///<paramname="Owner"></param>
  15. publicMouseHook(System.Windows.Forms.ControlOwner)
  16. {
  17. this._Owner=Owner;
  18. this._Owner.MouseDown+=newMouseEventHandler(this.Control_MouseDown);
  19. this._Owner.MouseMove+=newMouseEventHandler(this.Control_MouseMove);
  20. this._Owner.MouseUp+=newMouseEventHandler(this.Control_MouseUp);
  21. this._Owner.MouseEnter+=newEventHandler(this.Control_MouseEnter);
  22. this._BeginDrawControl=false;
  23. }
  24. #regionControl上的鼠标事件
  25. voidControl_MouseDown(objectsender,MouseEventArgse)
  26. {
  27. //如果没有选择控件,那么退出
  28. if(SettingService.Instance.SelectedToolBoxControl==null)
  29. {
  30. return;
  31. }
  32. this._CLickAtX=e.X;
  33. this._ClickAtY=e.Y;
  34. this._MoveAtX=e.X;
  35. this._MoveAtY=e.Y;
  36. this._BeginDrag=true;
  37. if(SettingService.Instance.SelectedToolBoxControl!=null)
  38. {
  39. this._BeginDrawControl=true;
  40. }
  41. else
  42. {
  43. this._BeginDrawControl=false;
  44. }
  45. }
  46. voidControl_MouseMove(objectsender,MouseEventArgse)
  47. {
  48. if(SettingService.Instance.SelectedToolBoxControl==null)
  49. {
  50. return;
  51. }
  52. if(this._BeginDrag)
  53. {
  54. //取消上次绘制的选择框
  55. intiLeft,iTop,iWidth,iHeight;
  56. Penpen;
  57. Rectanglerect;
  58. pen=newPen(this._Owner.BackColor);
  59. if(this._BeginDrawControl==true)
  60. {
  61. pen.DashStyle=System.Drawing.Drawing2D.DashStyle.Solid;
  62. pen.Width=2;
  63. }
  64. else
  65. {
  66. pen.DashStyle=System.Drawing.Drawing2D.DashStyle.Dot;
  67. }
  68. iLeft=this._CLickAtX<this._MoveAtX?this._CLickAtX:this._MoveAtX;
  69. iTop=this._ClickAtY<this._MoveAtY?this._ClickAtY:this._MoveAtY;
  70. iWidth=Math.Abs(this._MoveAtX-this._CLickAtX);
  71. iHeight=Math.Abs(this._MoveAtY-this._ClickAtY);
  72. rect=newRectangle(iLeft,iTop,iWidth,iHeight);
  73. this._Owner.CreateGraphics().DrawRectangle(pen,rect);
  74. //重新绘制选择框
  75. this._MoveAtX=e.X;
  76. this._MoveAtY=e.Y;
  77. pen=newPen(Color.Black);
  78. if(this._BeginDrawControl==true)
  79. {
  80. pen.DashStyle=System.Drawing.Drawing2D.DashStyle.Solid;
  81. pen.Width=2;
  82. }
  83. else
  84. {
  85. pen.DashStyle=System.Drawing.Drawing2D.DashStyle.Dot;
  86. }
  87. iLeft=this._CLickAtX<this._MoveAtX?this._CLickAtX:this._MoveAtX;
  88. iTop=this._ClickAtY<this._MoveAtY?this._ClickAtY:this._MoveAtY;
  89. iWidth=Math.Abs(this._MoveAtX-this._CLickAtX);
  90. iHeight=Math.Abs(this._MoveAtY-this._ClickAtY);
  91. rect=newRectangle(iLeft,iTop,iWidth,iHeight);
  92. this._Owner.CreateGraphics().DrawRectangle(pen,rect);
  93. }
  94. }
  95. voidControl_MouseUp(objectsender,MouseEventArgse)
  96. {
  97. this._BeginDrag=false;
  98. this._Owner.SuspendLayout();
  99. if(SettingService.Instance.SelectedToolBoxControl==null)
  100. {
  101. return;
  102. }
  103. //取消上次绘制的选择框
  104. intiLeft,iTop,iWidth,iHeight;
  105. Penpen;
  106. Rectanglerect;
  107. pen=newPen(this._Owner.BackColor);
  108. pen.DashStyle=System.Drawing.Drawing2D.DashStyle.Dot;
  109. iLeft=this._CLickAtX<this._MoveAtX?this._CLickAtX:this._MoveAtX;
  110. iTop=this._ClickAtY<this._MoveAtY?this._ClickAtY:this._MoveAtY;
  111. iWidth=Math.Abs(this._MoveAtX-this._CLickAtX);
  112. iHeight=Math.Abs(this._MoveAtY-this._ClickAtY);
  113. rect=newRectangle(iLeft,iTop,iWidth,iHeight);
  114. this._Owner.CreateGraphics().DrawRectangle(pen,rect);
  115. if(SettingService.Instance.SelectedToolBoxControl!=null)
  116. {
  117. AddControl(SettingService.Instance.SelectedToolBoxControl,rect);
  118. }
  119. else
  120. {
  121. //这里是拖动鼠标,选择控件,这里将会在后续的介绍中给出
  122. }
  123. this._Owner.Refresh();
  124. this._Owner.ResumeLayout();
  125. }
  126. voidControl_MouseEnter(objectsender,EventArgse)
  127. {
  128. if(SettingService.Instance.SelectedToolBoxControl!=null)
  129. {
  130. this._Owner.Cursor=Cursors.Cross;
  131. }
  132. else
  133. {
  134. this._Owner.Cursor=Cursors.Default;
  135. }
  136. }
  137. privatevoidAddControl(System.Windows.Forms.Controlcontrol,Rectanglerect)
  138. {
  139. try
  140. {
  141. control.Location=rect.Location;
  142. control.Size=rect.Size;
  143. control.Name=GetControlName(control);
  144. //因为对于DataTimePiker控件来说不能设置.Text为非日期型,所以忽略错误
  145. try
  146. {
  147. control.Text=GetControlType(control);
  148. }
  149. catch{}
  150. this._Owner.Controls.Add(control);
  151. control.Visible=true;
  152. this._Owner.Cursor=Cursors.Default;
  153. SettingService.Instance.SelectedToolBoxControl=null;
  154. }
  155. catch(Exceptione)
  156. {
  157. this._Owner.Cursor=Cursors.Default;
  158. SettingService.Instance.SelectedToolBoxControl=null;
  159. }
  160. }
  161. privatestringGetControlType(System.Windows.Forms.Controlctrl)
  162. {
  163. stringstrType=ctrl.GetType().ToString();
  164. stringstrControlType;
  165. string[]strArr=strType.Split(".".ToCharArray());
  166. strControlType=strArr[strArr.Length-1].Trim();
  167. returnstrControlType;
  168. }
  169. privatestringGetControlName(System.Windows.Forms.Controlcontrol)
  170. {
  171. //这里简单返回控件名,如果需要,可以通过修改这个函数做特殊处理
  172. returncontrol.GetType().Name;
  173. }
  174. #endregion
  175. }

另外Form代码如下,这里为了方便,我直接先使用几个Button来替代实际中的ToolBox:

  1. //在Form中增加几个Button,分别命名为cmdArrow,cmdLabel,cmdTextBox,cmdComboBox,cmdGroupBox
  2. publicpartialclassForm1:Form
  3. {
  4. privateMouseHook_MouseHook;
  5. publicForm1()
  6. {
  7. InitializeComponent();
  8. this._MouseHook=newMouseHook(this);
  9. }
  10. privatevoidcmdArrow_Click(objectsender,EventArgse)
  11. {
  12. SettingService.Instance.SelectedToolBoxControl=null;
  13. }
  14. privatevoidcmdLabel_Click(objectsender,EventArgse)
  15. {
  16. SettingService.Instance.SelectedToolBoxControl=newLabel();
  17. }
  18. privatevoidcmdTextBox_Click(objectsender,EventArgse)
  19. {
  20. SettingService.Instance.SelectedToolBoxControl=newTextBox();
  21. }
  22. privatevoidcmdComboBox_Click(objectsender,EventArgse)
  23. {
  24. SettingService.Instance.SelectedToolBoxControl=newComboBox();
  25. }
  26. privatevoidcmdGroupBox_Click(objectsender,EventArgse)
  27. {
  28. SettingService.Instance.SelectedToolBoxControl=newGroupBox();
  29. }
  30. }

上面就是简单实现拖动增加控件的方法,如果要在GroupBox中增加控件的话,只需要再new 一个MouseHook,如:new MouseHook(GroupBoxControl)这样就可以了的。