正常情况下,在开发.NET Windows应用程序时,我们都会用到System.Windows.Forms名字空间的控件。可供我们使用的控件很多,从Label、TextBox等简单的控件到MonthCalendar、ColorDialog等功能更丰富、更复杂的控件。尽管这些控件对于我们需要开发的大多数Windows应用程序已经足够好了,但有时我们也需要自己开发一些System.Windows.Forms名字空间不包括的控件。本篇文章将讲述如何使用VB.NET创建定制控件,尤其是在需要提供自己的图形用户接口时。
开发定制的控件并不困难。在开发定制控件时,我们可以使用现有的控件,或者对Control或UserControl类进行扩展。结合使用现有的控件使我们减少提供接口的麻烦,扩展Control或UserControl类意味着我们需要覆盖OnPaint方法,自己绘制图形用户接口。本篇文章中,我们由UserControl类派生了一个定制控件,UserControl类本身也是由继承Control类而生成的。因此读者需要对这二个类有一定的了解。
Control类非常重要,因为它是Windows可视化组件的父类,我们开发的定制类将是Control类的一个子类。我们的定制类一般不会直接由Control类派生而成,相反,一般是对UserControl类进行扩展。
Control类 Control类提供向Windows应用程序用户显示信息的类所要求的基本功能,它处理用户通过键盘和鼠标进行的输入,以及消息的分配和安全。更重要的是,Control类定义了控件的范围(位置和大小),尽管它不实现控件的绘制。
Windows表单控件使用了环境属性,因此其子控件的显示与其周围环境相似。缺省情况下,环境属性是由其父控件获得的,如果类没有父控件或者其环境属性没有设置,则控件试图通过Site属性设置环境属性的值。如果控件没有确定位置,不支持环境属性,或者AmbientProperties对象的属性没有设置,控件就会使用缺省值。一般情况下,控件的环境特性表示控件的一个特征,例如BackColor,它会传递给子控件。例如,缺省情况下,Button控件将具有与其父表单控件相同的BackColor环境属性。
许多Control类的属性、方法和事件都会不加变化地传递给子类。
Control类的属性 下面是Control类的一些最重要的属性
BackColor
控件的背景颜色,是由一个System.Drawing.Color对象表示的。我们可以使用如下所示的代码将一个System.Drawing.Color对象赋给该属性:
control.BackColor = System.Drawing.Color.Red
Enabled
一个表示该控件是否可用的布尔型值,缺省情况下其值为True。
Location
控件的左上角在其窗口中的位置,由一个System.Drawing.Point对象表示。
Name
控件的名字。
Parent
返回控件的父控件或容器的引用。例如,在一个表单中添加的控件的父控件就是该表单,下面的代码将Button1控件所在的表单的标题栏改为“Thank you.”:
Button1.Parent.Text = "Thank you."
Size
控件的大小,由System.Drawing.Size对象表示。
Text
与控件相关的字符串。例如,在Label控件中,Text属性就是显示在标签体上的字符串。
Control类的方法 下面是一些Control类最经常使用的方法
BringToFront
如果该控件在其他一些控件下面,完整地显示该控件。换一句话说,这一方法能够显示一个完整的控件。
CreateGraphics
获取控件的System.Drawing.Graphics对象,我们可以在其上利用System.Drawing.Graphics类的各种方法进行显示。例如,下面的代码获取名字为Button1的控件的Graphics图像,然后在按钮上划一条对角的绿线:
Imports System.Drawing
Dim graphics As Graphics = Button1.CreateGraphics
Dim pen As Pen = New Pen(Color.Green)
graphics.DrawLine(pen, 0, 0, _
Button1.Size.Width, Button1.Size.Height)
但是,用这种方法在控件上画图,所画的图像不是“永久”的。当控件或者包含控件的表单被重画时,用这种方式画的图像就会消失。
Focus
将焦点给予该控件,使它成为活动控件
Hide
将控件的Visible属性设置为False,使它不被显示出来。
GetNextControl
按Tab键控制次序返回下一个控件。
OnXXX
触发XXX事件。这里的XXX可以是Click、ControlAdded、ControlRemoved、DoubleClick、DragDrop、DragEnter、DragLeave、DragOver、Enter、GotFocus、KeyDown、KeyPress、KeyUp、LostFocus、MouseDown、MouseEnter、MouseHover、MouseLeave、MouseMove、MouseUp、Move、Paint、Resize和TextChanged。例如,调用控件的OnClick方法就会触发其Click事件。
Show
将控件的Visible属性设置为True,以显示该控件。
UserControl类 UserControl类提供一个可以用来创建其他控件的空控件,它是Control类的一个间接子类。由该控件派生的对象如下所示:
·System.Object
·System.MarshalByRefObject
·System.ComponentModel.Component
·System.Windows.Forms.Control
·System.Windows.Forms.ScrollableControl
·System.Windows.Forms.ContainerControl
·System.Windows.Forms.UserControl
UserControl类从ContainerControl类继承所有的标准位置和与内存处理有关的代码。在用户定制控件中还需要这些代码。
RoundButton控件 有了Control和UserControl这二个类,开发定制的Windows控件就是轻而易举的了。我们的定制类是通过继承UserControl类而生成的,由于UserControl也是由继承Control类而生成的,我们的定制类将会继承Control类的所有有用的方法、属性和事件。例如,由于是继承Control类生成的,我们的定制类会自动地拥有事件处理程序。
在开发定制控件时特别重要的一个问题是如何显示定制控件的用户界面。无论如何组织定制控件,需要注意的是,定制控件有时会重新显示。因此,当定制控件重绘时,必须重新绘制用户界面。考虑到控件每次重绘时,都会调用Control类的OnPaint方法,使用新的绘制定制控件用户界面的OnPaint方法覆盖该方法就能保证定制控件的保持一定的外观。
表1中的代码是一个名称为RoundButton的控件,在图1中,表单上有一个RoundButton定制控件,表2是其代码。我们需要作的工作基本上就是覆盖OnPaint方法。系统向该方法传递一个PaintEventArgs对象,从该方法中我们可以获得控件的System.Drawing.Graphics对象,然后使用它的方法绘制定制控件的用户界面。
表1:RoundButton控件
Imports System.Windows.Forms
Imports System.Drawing
Public Class RoundButton : Inherits UserControl
Public BackgroundColor As Color = Color.Blue
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
Dim graphics As Graphics = e.Graphics
Dim penWidth As Integer = 4
Dim pen As Pen = New Pen(Color.Black, 4)
Dim fontHeight As Integer = 10
Dim font As Font = New Font("Arial", fontHeight)
Dim brush As SolidBrush = New SolidBrush(BackgroundColor)
graphics.FillEllipse(brush, 0, 0, Width, Height)
Dim textBrush As SolidBrush = New SolidBrush(Color.Black)
graphics.DrawEllipse(pen, CInt(penWidth / 2), _
CInt(penWidth / 2), Width - penWidth, Height - penWidth)
graphics.DrawString(Text, font, textBrush, penWidth, _
Height / 2 - fontHeight)
End Sub
End Class
表1中的代码非常地简单,简直令人不能相信。我们的定制类只有一个方法:OnPaint。简单地说,该方法传递一个PaintEventArgs对象,从中我们可以获得System.Drawing.Graphics对象。这一Graphics对象表示我们的定制控件的绘制区,无论在该Graphics对象上绘制什么东西,它都会显示为定制用户控件的界面。
在Windows的编程中,绘制图形时需要用到笔、画笔等对象,要书写文本,就需要字体对象。下面OnPaint方法中的代码创建了一个宽度为4的System.Drawing.Pen对象:
Dim penWidth As Integer = 4
Dim pen As Pen = New Pen(Color.Black, 4)
然后再创建一个高度为10的Arial Font对象:
Dim fontHeight As Integer = 10
Dim font As Font = New Font("Arial", fontHeight)
我们要作的最后一步的准备工作是实例化一个SolidBrush对象,并使其颜色与backgroundColor字段的颜色一致。
Dim brush As SolidBrush = New SolidBrush(backgroundColor)
现在我们就可以来画了。对于控制的底部,我们可以使用Graphics类的FillEllipse方法,圆的高和宽与控件的高和宽是相同的:
graphics.FillEllipse(brush, 0, 0, Width, Height)
然后,我们可以实例化另一个画笔,用来完成对文本的绘制:
Dim textBrush As SolidBrush = New SolidBrush(Color.Black)
至于圆形,我们可以使用Graphics类的DrawEllipse方法:
graphics.DrawEllipse(pen, Cint(penWidth/2), _
CInt(penWidth/2), Width - penWidth, Height - penWidth)
最后,我们使用DrawString方法在Graphics对象上绘制文本内容:
graphics.DrawString(Text, font, textBrush, penWidth, _
Height / 2 - fontHeight)
我们最后得到的RoundButton控件
好了,把控件的代码编译为一个.DLL文件,它就可以供我们随时使用了。
表2中的代码是一个调用了RoundButton控件、名称为MyForm的表单。
表2:RoundButton控件的调用
Public Class MyForm
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Private WithEvents roundButton As RoundButton
Public Sub New()
MyBase.New()
'这个调用是Windows Form Designer所要求的
InitializeComponent()
'在InitializeComponent()调用后,可以添加任意的实例化代码
End Sub
'表单覆盖,整理组件列表
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Windows Form Designer所要求的
Private components As System.ComponentModel.IContainer
'注意:下面的过程是Windows Form Designer所要求的,
'可以使用Windows Form Designer对它进行修改,
'但不要使用软件编辑程序进行修改
Private Sub InitializeComponent()
'
'MyForm
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 273)
Me.Name = "MyForm"
Me.Text = "Using Custom Control"
roundButton = New RoundButton()
AddHandler roundButton.Click, AddressOf roundButton_Click
roundButton.Text = "Click Here!"
roundButton.BackgroundColor = System.Drawing.Color.White
roundButton.Size = New System.Drawing.Size(80, 80)
roundButton.Location = New System.Drawing.Point(100, 30)
Me.Controls.Add(roundButton)
End Sub
#End Region
Private Sub roundButton_Click(ByVal source As Object, ByVal e As EventArgs)
MessageBox.Show("Thank you.")
End Sub
Public Shared Sub Main()
Dim form As MyForm = New MyForm()
Application.Run(form)
End Sub
End Class
在InitializeComponent方法中,表单对一个RoundButton对象进行实例化,并将RoundButton控件的Click事件与事件处理程序roundButton_Click连接起来:
roundButton = New RoundButton()
AddHandler roundButton.Click, AddressOf roundButton_Click
需要注意的是,由于我们没有在RoundButton类中定义任何事件,因此它的事件处理能力是继承Control类而得来的。
我们下一步要作的就是设置RoundButton控件的一些属性了:
roundButton.Text = "Click Here!"
roundButton.BackgroundColor = System.Drawing.Color.White
roundButton.Size = New System.Drawing.Size(80, 80)
roundButton.Location = New System.Drawing.Point(100, 30)
最后,将roundButton控件添加到表单的控件集中:
Me.Controls.Add(roundButton)
当用户点击控件触发Click事件时,Click事件调用roundButton_Click事件处理程序,显示一个消息框:
Private Sub roundButton_Click(ByVal source As Object, _
ByVal e As EventArgs)
MessageBox.Show("Thank you.")
End Sub
结论 在本篇文章中,我们介绍了开发定制控件时需要理解的System.Windows.Forms名字空间中二个重要的类:Control和UserControl。另外,我们还介绍了如何通过直接扩充UserControl类开发自己的定制控件以及如何在Windows表单中使用定制控件。