所谓复合控件,就是将现有的各种控件组合起来,形成一个新的控件,来满足用户的需求。
在实际项目中,FlexGrid控件内,单元格内有多个自定义图标的按钮和显示用的文字,需求是该单元格必须和普通单元保持一致,并且按钮事件能触发。如下图,其中红色的M和▲是能点击的按钮。
要实现这么一个功能,Winform自定义控件的三种方法中,至少有两种方法可以实现(三种方法即复合控件、扩展控件、自定义控件)。考虑开发时间和后期维护的问题,最终选择了用复合控件。
创建复合控件有两种方式,可以创建一个Windows控件库,然后添加用户控件类;还可以在现有Windows应用程序中添加用户控件类。这里选择后者。
步骤概要:
1. 向复合控件中添加控件组合;
2. 添加属性,方便调用者控制复合控件中的控件属性;
3. 定义委托和事件;
4. 重写Refresh方法,优化排版;
5. 在复合控件内的控件事件中注册自定义的事件,引用控件。
1. 向复合控件中添加控件组合
复合控件由两个Label和两个PictureBox组成,Layout如下图。
2. 添加属性,方便调用者控制复合控件中的控件属性
这里可以是内部控件的文本属性,也可以是Visible、背景色等属性。其实可以在调用处直接引用到内部控件的属性,而不必在复合控件中单独定义,这里只是方便调用,使整个复合控件看起来更像一个整体。
1: '''
2: ''' 枠時刻
3: '''
4: ''' 枠時刻的Text
5: ''' 枠時刻的Text
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: '''
16: ''' Chance連番
17: '''
18: ''' Chance連番的Text
19: ''' Chance連番的Text
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: '''
30: ''' Chance連番的表示、非表示
31: '''
32: ''' Chance連番的表示、非表示Flag
33: ''' Chance連番的表示、非表示Flag
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: '''
2: ''' Multi的Event
3: '''
4: Public Event evtMulti As MultiHandler
5: '''
6: ''' 委托
7: '''
8: ''' Tag
9: Public Delegate Sub MultiHandler(ByVal oIndex As Object)
4. 重写Refresh方法,优化排版
由于Label的文字数不定,按钮在某些情况下需要影藏,为保证复合控件排版合理、美观,当内部控件的属性变化时,需要重新调整布局。Refresh方法属于控件顶层Control类,作用是强制控件使其工作区无效并立即重绘自己和任何子控件。这里重写Refresh方法,在方法内根据各子控件的属性,调整布局,并调整整个复合控件的大小。
1: '''
2: ''' Refresh方法
3: '''
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: '''
2: ''' Multi按钮按下
3: '''
4: ''' 事件实例
5: ''' 事件数据
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: '''
2: ''' Multi按钮处理方法
3: '''
4: ''' Index
5: Private Sub btnMultiClick(ByVal oIndex As Object)
6:
7: ' 业务逻辑处理
8:
9: End Sub
以上就是创建一个复合控件的基本步骤了。在实际项目中,需求是千变万化的,根据具体的情况,选择合适的实现方式,结合自身所掌握的技术知识,灵活运用,是一个程序员必备的素质。更多的时候,要有学习新技术、新方法的勇气,敢于突破自己,超越自我,如此方能在浩渺的代码世界里得到升华。