Winform自定义控件之复合控件开发

 

所谓复合控件,就是将现有的各种控件组合起来,形成一个新的控件,来满足用户的需求。

在实际项目中,FlexGrid控件内,单元格内有多个自定义图标的按钮和显示用的文字,需求是该单元格必须和普通单元保持一致,并且按钮事件能触发。如下图,其中红色的M和▲是能点击的按钮。

test

要实现这么一个功能,Winform自定义控件的三种方法中,至少有两种方法可以实现(三种方法即复合控件、扩展控件、自定义控件)。考虑开发时间和后期维护的问题,最终选择了用复合控件。

创建复合控件有两种方式,可以创建一个Windows控件库,然后添加用户控件类;还可以在现有Windows应用程序中添加用户控件类。这里选择后者。

 

步骤概要:

1. 向复合控件中添加控件组合;

2. 添加属性,方便调用者控制复合控件中的控件属性;

3. 定义委托和事件;

4. 重写Refresh方法,优化排版;

5. 在复合控件内的控件事件中注册自定义的事件,引用控件。

 

1. 向复合控件中添加控件组合

复合控件由两个Label和两个PictureBox组成,Layout如下图。

test1

 

2. 添加属性,方便调用者控制复合控件中的控件属性

这里可以是内部控件的文本属性,也可以是Visible、背景色等属性。其实可以在调用处直接引用到内部控件的属性,而不必在复合控件中单独定义,这里只是方便调用,使整个复合控件看起来更像一个整体。

   1:      ''' <summary>
   2:      ''' 枠時刻
   3:      ''' </summary>
   4:      ''' <value>枠時刻的Text</value>
   5:      ''' <returns>枠時刻的Text</returns>
   6:      Public Property WakuTimeText() As String
   7:          Get
   8:              Return Me.lblWakuTime.Text
   9:          End Get
  10:          Set(ByVal value As String)
  11:              Me.lblWakuTime.Text = value
  12:          End Set
  13:      End Property
  14:   
  15:      ''' <summary>
  16:      ''' Chance連番
  17:      ''' </summary>
  18:      ''' <value>Chance連番的Text</value>
  19:      ''' <returns>Chance連番的Text</returns>
  20:      Public Property ChanceRenBanText() As String
  21:          Get
  22:              Return Me.lblChanceRenBan.Text
  23:          End Get
  24:          Set(ByVal value As String)
  25:              Me.lblChanceRenBan.Text = value
  26:          End Set
  27:      End Property
  28:   
  29:      ''' <summary>
  30:      ''' Chance連番的表示、非表示
  31:      ''' </summary>
  32:      ''' <value>Chance連番的表示、非表示Flag</value>
  33:      ''' <returns>Chance連番的表示、非表示Flag</returns>
  34:      Public Property ChanceRenBanVisible() As Boolean
  35:          Get
  36:              Return Me.lblChanceRenBan.Visible
  37:          End Get
  38:          Set(ByVal value As Boolean)
  39:              Me.lblChanceRenBan.Visible = value
  40:          End Set
  41:      End Property

 

3. 定义委托和事件

如果事件不需要生成数据,可以基于EventHandler委托定义事件。EventHandler是一个预定义的委托,专用于表示不生成数据的事件的事件处理程序方法。这里因为需要用户数据传入,采用自定义委托。

   1:      ''' <summary>
   2:      ''' Multi的Event
   3:      ''' </summary>
   4:      Public Event evtMulti As MultiHandler
   5:      ''' <summary>
   6:      ''' 委托
   7:      ''' </summary>
   8:      ''' <param name="oIndex">Tag</param>
   9:      Public Delegate Sub MultiHandler(ByVal oIndex As Object)

 

4. 重写Refresh方法,优化排版

由于Label的文字数不定,按钮在某些情况下需要影藏,为保证复合控件排版合理、美观,当内部控件的属性变化时,需要重新调整布局。Refresh方法属于控件顶层Control类,作用是强制控件使其工作区无效并立即重绘自己和任何子控件。这里重写Refresh方法,在方法内根据各子控件的属性,调整布局,并调整整个复合控件的大小。

   1:      ''' <summary>
   2:      ''' Refresh方法
   3:      ''' </summary>
   4:      Public Overrides Sub Refresh()
   5:   
   6:          Dim iWidth As Integer = Nothing
   7:          Dim iHeight As Integer = Nothing
   8:   
   9:          Me.lblWakuTime.Location = New Point(iWidth, 0)
  10:          iWidth += Me.lblWakuTime.Size.Width
  11:          If Me.lblWakuTime.Size.Height > iHeight Then
  12:              iHeight = Me.lblWakuTime.Size.Height
  13:          End If
  14:   
  15:          If Me.picMulti.Visible Then
  16:              Me.picMulti.Location = New Point(iWidth, 4)
  17:              iWidth += Me.picMulti.Size.Width
  18:          End If
  19:          If Me.picMulti.Size.Height > iHeight Then
  20:              iHeight = Me.picMulti.Size.Height
  21:          End If
  22:   
  23:          If Me.lblChanceRenBan.Visible Then
  24:              Me.lblChanceRenBan.Location = New Point(iWidth, 0)
  25:              iWidth += Me.lblChanceRenBan.Size.Width
  26:          End If
  27:          If Me.lblChanceRenBan.Size.Height > iHeight Then
  28:              iHeight = Me.lblChanceRenBan.Size.Height
  29:          End If
  30:   
  31:          If Me.picWakuTori.Visible Then
  32:              Me.picWakuTori.Location = New Point(iWidth, 4)
  33:              iWidth += Me.picWakuTori.Size.Width
  34:          End If
  35:          If Me.picWakuTori.Size.Height > iHeight Then
  36:              iHeight = Me.picWakuTori.Size.Height
  37:          End If
  38:   
  39:          Me.Size = New Size(iWidth, iHeight)
  40:   
  41:      End Sub

 

5. 在复合控件内的控件事件中注册自定义的事件,引用控件

注册事件及在引用处关联处理方法,根据传入参数,加入业务处理逻辑,此乃本次复合控件创建的重心。

用户点击复合控件内的按钮,首先是触发复合控件内的按钮事件;然后在按钮事件中激发自定义的事件,根据自定义的委托,传入相关的参数;最后在引用的地方关联自定义事件的处理方法,执行业务逻辑。复合控件内的自定义事件,同属性类似,在引用处的设计器的属性里可以看得到。以下是自定义事件的处理流程:

点击M按钮→触发复合控件内的按钮事件→激发自定义事件,并传入子控件属性作参数→在引用处关联自定义事件处理方法,执行业务逻辑。

● 复合控件内的按钮事件激发自定义事件

   1:      ''' <summary>
   2:      ''' Multi按钮按下
   3:      ''' </summary>
   4:      ''' <param name="sender">事件实例</param>
   5:      ''' <param name="e">事件数据</param>
   6:      Private Sub picMulti_Click(
   7:                                ByVal sender As Object,
   8:                                ByVal e As EventArgs
   9:                                ) Handles picMulti.Click
  10:   
  11:          ' 注册Multi事件
  12:          RaiseEvent evtMulti(Me.picMulti.Tag)
  13:   
  14:      End Sub

● 引用处画面启动时,初始化复合控件,关联自定义事件处理方法

   1:          ' 实例化复合控件
   2:          Dim oUnionControl As UnionControl = New UnionControl()
   3:   
   4:          ' 设置Multi按钮可见
   5:          oUnionControl.MultiVisible = True
   6:   
   7:          ' 关联事件处理方法(btnMultiClick)
   8:          AddHandler oUnionControl.evtMulti, AddressOf btnMultiClick

● 处理方法

   1:      ''' <summary>
   2:      ''' Multi按钮处理方法
   3:      ''' </summary>
   4:      ''' <param name="oIndex">Index</param>
   5:      Private Sub btnMultiClick(ByVal oIndex As Object)
   6:   
   7:          ' 业务逻辑处理
   8:   
   9:      End Sub

 

以上就是创建一个复合控件的基本步骤了。在实际项目中,需求是千变万化的,根据具体的情况,选择合适的实现方式,结合自身所掌握的技术知识,灵活运用,是一个程序员必备的素质。更多的时候,要有学习新技术、新方法的勇气,敢于突破自己,超越自我,如此方能在浩渺的代码世界里得到升华。

 

posted @ 2013-04-08 09:35  静水无踪  阅读(3069)  评论(0编辑  收藏  举报