使用VB.NET开发自定义Windows控件
【打印文章】
王凌峰 编译
前言
Microsoft® Visual Basic® 的组件支持历来都是它的一大卖点,于是第三方软件开发商们纷纷开发出各种具有新功能性的可视控件 (也有少数非可视控件) 供 Visual Basic 程序员选用。这种特殊的 Visual Basic 开发形式创造了无数的第三方控件——有的是共享软件/自由软件,有的则被放到柜台上销售。现在,人们甚至可以直接用 Visual Basic 开发自己的可视/非可视组件了。于是,组件的数量迅速增长,其中相当一部分都是程序员 (或者开发小组) 为针对自己的开发任务设计的。
注意 你或你的开发小组过去购买的 Microsoft ActiveX 控件往往无须修改或重写就能直接移植到微软 .NET 环境下。具体而言,只要进入 Microsoft Visual Studio® .NET 的 IDE (集成开发环境) 环境,依次从菜单中选择:工具 Tool -> 自定义工具箱 Customize Toolbox) ,或者使用 .NET 框架实用程序 Aximp.exe (ActiveX 控件导入程序) ,就能让 .NET 应用程序中调用现成的 ActiveX 控件了。可是,一旦某个控件在 .NET 环境下工作不正常,它的作者恐怕就应该考虑升级该控件了。所以,为了能在 .NET 环境中正常使用购来的第三方 ActiveX 控件,就应该到开发商的 Web 网站去看看它有没有出升级版或者 .NET 版。
在 .NET 编程世界里,人们对自定义 UI 组件的需求依然存在,只不过它们的创建过程有所不同。本文将探讨两个问题:为什么要创建自己的 Microsoft Windows® 控件?在 Visual Basic .NET 中开发控件时有哪些方面不同于以往的 5.0 / 6.0 版?
为什么要开发你自己的控件?
为了限制 Windows 窗体TextBox 控件的文本类型,可以在窗体代码中添加该控件的KeyPress 事件处理程序,以拦截用户的每次击键并检查该键对应的字符能否进入 TextBox :
Private Sub TextBox1_KeyPress(ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyPressEventArgs) _
Handles TextBox1.KeyPress
If Not Char.IsDigit(e.KeyChar) Then
e.Handled = True
Else
e.Handled = False
End If
End Sub
注意 单纯依靠捕捉击键事件是无法确保输入 TextBox 的文本全是数字的,因为用户有时不是直接向 TextBox 中敲入字符,而是通过剪贴板粘贴字符给 TextBox ;何况 TextBox 文本的初值就有可能包含非法的字符。某些其它事件比如 TextChanged 等,或许能够捕捉到更多非法输入,但我更喜欢用 Validating 或者 Leave 事件,它们是在用户离开输入控件之后才对 TextBox 进行字符合法性检查。这么做诚然放弃了对用户输入的即时反应,却允许用户首先通过剪贴板输入“轻度犯规”的文本字符串 ,比如在禁止空格的输入框中粘贴 “3425 2343 2342 2342”,然后手工纠正输入框里的“犯规”字符。
向控件中手工添加事件处理程序代码并不太难,可是当你面临更复杂的编程任务,比如检验邮寄地址或者汽车的 VIN # (车辆识别号码) 的字符合法性时,你还会感到如此轻松吗?此时你会希望把同一段事件处理程序用于多个窗体甚至多个项目,或者将它提供给开发小组的其他成员共享。然而,提取窗体中的代码片段,连同安装指南和控件的命名规则一起发布,却是一个恶梦的开端。好在天无绝人之路,你只要把它连同一个自定义控件发布,就不会遭遇这种恶梦了,因为此时用户界面和相关代码都位于独立的组件中,而组件的发布相对要容易得多。通过组件发布的代码片段在升级上也方便些:你只需发布新版的组件即可,再也不必通过种种渠道公布新的代码片段让程序员手工覆盖原先的代码了!
继承性如何改变了控件的开发?
在 .NET 中的控件开发已经和 Visual Basic 6.0 大相径庭。其根本原因,就是 .NET 引入了继承性。在 Visual Basic 6.0 中,你只能不用控件或者直接引用现成的控件来实现各种功能性。例如:为了创建前面提到的自定义文本输入框,你就要新建一个 ActiveX 控件,然后向其中增加一个 TextBox 。
注意 人们通常把这种编程思路称为“容器” (containment) 或者“委托” (delegation)。在 Visual Basic 6.0 中,用于模拟继承机制的非控件类也可以采用这种思路。
此时,新建的 ActiveX 控件并不会如你所愿自动获得 TextBox 的某些属性 (比如 Text 属性);这些属性只能由你编码实现。更糟的是,你必须用许多代码来确保 TextBox 始终占据整个窗体;你还得为新控件设计 resizing 事件处理程序。当然,经过一番折腾,你总会完成该控件的设计任务的,何况还有 ActiveX 控件界面向导 ( Visual Basic 6.0 附加程序之一,如图 1 所示) 能减轻你的负担。可是在 .NET 环境下,整个任务的完成思路都会变得完全不同。
图 1. Visual Basic 6.0 提供了一个附加程序 (add-in) ,它能自动添加和映射控件属性,以简化控件的开发。
继承性能避免控件开发中的某些重复代码,因为它能让 .NET 控件直接获得任何其它控件的功能性。例如:为了创建自己的 TextBox 控件,你可以继承现有的 TextBox 控件,而不是 UserControl 控件。新控件继承了基类控件的全部功能性,因此你只需要对基类控件中没有的功能性编码即可。下面举一个实际的例子。以下代码能够创建一个自定义 TextBox 控件,它只允许用户输入数字字符:
注意 为了运行这段代码,你只需在“Windows 应用程序”模板下新建一个 Visual Basic .NET 项目,然后就能在 IDE 自动生成的空白窗体中试验新控件了。在项目中新建一个类 NumericTextBox ,用下面的代码替换 NumericTextBox 类文件的内容,编译该项目。最后,在菜单中选择工具->自定义工具箱,选中先前编译项目得到的 .exe 文件,就能把新控件添加到工具箱了。
Public Class NumericTextBox
Inherits System.Windows.Forms.TextBox
Protected Overrides Sub OnKeyPress(ByVal e As _
System.Windows.Forms.KeyPressEventArgs)
If Not Char.IsDigit(e.KeyChar) Then
e.Handled = True
Else
e.Handled = False
End If
End Sub
End Class
对本例来说,以上代码已经足够了。如果你还觉得它不够完善的话,请改用下列代码,它运用一种奇妙的布尔逻辑减少了代码行数:
Public Class NumericTextBox
Inherits System.Windows.Forms.TextBox
Protected Overrides Sub OnKeyPress(ByVal e As _
System.Windows.Forms.KeyPressEventArgs)
e.Handled = Not Char.IsDigit(e.KeyChar)
End Sub
End Class
现在,你的新控件已经正确显示在窗体中了。它象 TextBox 一样处理事件,并且拥有与 TextBox 一样的方法、属性。你甚至不需更多的编码就能实现对新控件的数据绑定,因为这也是基类控件 TextBox 的功能性之一。
注意 本控件对用户输入的要求十分苛刻:它只允许输入 0 至 9 的数字,也就是说,数字中的逗号、小数点甚至负号都是非法字符。我将在下一篇文章中介绍一个功能更强的输入验证程序。
在 Visual Basic 6.0 中设计本控件时,核心代码会和本范例一样长,可是用于处理控件的 resizing 事件和实现 TextBox 组件属性的代码也会有这么长。由此可见,.NET 提供的继承性能够大大精简源代码。单凭这一点,.NET 就已经令人叹服了,何况它还有许多其它优越性。更奇妙的是,凡是要求使用某一控件的地方,都能改用继承该控件而来的新控件。例如:在任何例程中要求 TextBox 的地方都能用你的 NumericTextBox 控件。不仅如此,从现有控件,而不是从 UserControl 类继承而来的新控件,不但具备基类控件的所有功能性,还能象基类控件一样使用继承得到的属性、方法和事件。因此,任何程序员只要学过标准的 TextBox 控件,就知道如何使用 NumericTextBox 控件。允许继承现有的类/控件,是从Visual Basic 6.0 到 .NET 的一个重大飞跃,可是 .NET 的优点又何止于此!只要你认真学习本系列文章所提供的范例,你就会发现,在 .NET 环境下Windows 窗体控件不但拥有不少强大的功能,而且它们的创建也比在老版本 Visual Basic 中容易得多。
总结
无论对于个人、开发小组,还是软件开发商,自定义控件的开发都是功能最强大的组件技术之一。众所周知,把用户界面与功能性以软件包的形式进行发布,始终是Visual Basic 等可视化开发工具立于不败之地的重要因素;在新版的可视化工具里仍然延续着该理念。到了 .NET 时代,控件的开发思路已经大不相同了,比如:它引入了继承等新特性。好在这些新特性都是对程序员有利的,因为它们有效地减少了人们的编码负担,进而简化了自定义控件的创建过程。本文以若干控件为例向读者介绍了自定义控件的创建过程,以及控件开发中的某些重要技术。当然,我还希望读者能从这些范例中学会如何创建自己的新控件。
前言
Microsoft® Visual Basic® 的组件支持历来都是它的一大卖点,于是第三方软件开发商们纷纷开发出各种具有新功能性的可视控件 (也有少数非可视控件) 供 Visual Basic 程序员选用。这种特殊的 Visual Basic 开发形式创造了无数的第三方控件——有的是共享软件/自由软件,有的则被放到柜台上销售。现在,人们甚至可以直接用 Visual Basic 开发自己的可视/非可视组件了。于是,组件的数量迅速增长,其中相当一部分都是程序员 (或者开发小组) 为针对自己的开发任务设计的。
注意 你或你的开发小组过去购买的 Microsoft ActiveX 控件往往无须修改或重写就能直接移植到微软 .NET 环境下。具体而言,只要进入 Microsoft Visual Studio® .NET 的 IDE (集成开发环境) 环境,依次从菜单中选择:工具 Tool -> 自定义工具箱 Customize Toolbox) ,或者使用 .NET 框架实用程序 Aximp.exe (ActiveX 控件导入程序) ,就能让 .NET 应用程序中调用现成的 ActiveX 控件了。可是,一旦某个控件在 .NET 环境下工作不正常,它的作者恐怕就应该考虑升级该控件了。所以,为了能在 .NET 环境中正常使用购来的第三方 ActiveX 控件,就应该到开发商的 Web 网站去看看它有没有出升级版或者 .NET 版。
在 .NET 编程世界里,人们对自定义 UI 组件的需求依然存在,只不过它们的创建过程有所不同。本文将探讨两个问题:为什么要创建自己的 Microsoft Windows® 控件?在 Visual Basic .NET 中开发控件时有哪些方面不同于以往的 5.0 / 6.0 版?
为什么要开发你自己的控件?
为了限制 Windows 窗体TextBox 控件的文本类型,可以在窗体代码中添加该控件的KeyPress 事件处理程序,以拦截用户的每次击键并检查该键对应的字符能否进入 TextBox :
Private Sub TextBox1_KeyPress(ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyPressEventArgs) _
Handles TextBox1.KeyPress
If Not Char.IsDigit(e.KeyChar) Then
e.Handled = True
Else
e.Handled = False
End If
End Sub
注意 单纯依靠捕捉击键事件是无法确保输入 TextBox 的文本全是数字的,因为用户有时不是直接向 TextBox 中敲入字符,而是通过剪贴板粘贴字符给 TextBox ;何况 TextBox 文本的初值就有可能包含非法的字符。某些其它事件比如 TextChanged 等,或许能够捕捉到更多非法输入,但我更喜欢用 Validating 或者 Leave 事件,它们是在用户离开输入控件之后才对 TextBox 进行字符合法性检查。这么做诚然放弃了对用户输入的即时反应,却允许用户首先通过剪贴板输入“轻度犯规”的文本字符串 ,比如在禁止空格的输入框中粘贴 “3425 2343 2342 2342”,然后手工纠正输入框里的“犯规”字符。
向控件中手工添加事件处理程序代码并不太难,可是当你面临更复杂的编程任务,比如检验邮寄地址或者汽车的 VIN # (车辆识别号码) 的字符合法性时,你还会感到如此轻松吗?此时你会希望把同一段事件处理程序用于多个窗体甚至多个项目,或者将它提供给开发小组的其他成员共享。然而,提取窗体中的代码片段,连同安装指南和控件的命名规则一起发布,却是一个恶梦的开端。好在天无绝人之路,你只要把它连同一个自定义控件发布,就不会遭遇这种恶梦了,因为此时用户界面和相关代码都位于独立的组件中,而组件的发布相对要容易得多。通过组件发布的代码片段在升级上也方便些:你只需发布新版的组件即可,再也不必通过种种渠道公布新的代码片段让程序员手工覆盖原先的代码了!
继承性如何改变了控件的开发?
在 .NET 中的控件开发已经和 Visual Basic 6.0 大相径庭。其根本原因,就是 .NET 引入了继承性。在 Visual Basic 6.0 中,你只能不用控件或者直接引用现成的控件来实现各种功能性。例如:为了创建前面提到的自定义文本输入框,你就要新建一个 ActiveX 控件,然后向其中增加一个 TextBox 。
注意 人们通常把这种编程思路称为“容器” (containment) 或者“委托” (delegation)。在 Visual Basic 6.0 中,用于模拟继承机制的非控件类也可以采用这种思路。
此时,新建的 ActiveX 控件并不会如你所愿自动获得 TextBox 的某些属性 (比如 Text 属性);这些属性只能由你编码实现。更糟的是,你必须用许多代码来确保 TextBox 始终占据整个窗体;你还得为新控件设计 resizing 事件处理程序。当然,经过一番折腾,你总会完成该控件的设计任务的,何况还有 ActiveX 控件界面向导 ( Visual Basic 6.0 附加程序之一,如图 1 所示) 能减轻你的负担。可是在 .NET 环境下,整个任务的完成思路都会变得完全不同。
图 1. Visual Basic 6.0 提供了一个附加程序 (add-in) ,它能自动添加和映射控件属性,以简化控件的开发。
继承性能避免控件开发中的某些重复代码,因为它能让 .NET 控件直接获得任何其它控件的功能性。例如:为了创建自己的 TextBox 控件,你可以继承现有的 TextBox 控件,而不是 UserControl 控件。新控件继承了基类控件的全部功能性,因此你只需要对基类控件中没有的功能性编码即可。下面举一个实际的例子。以下代码能够创建一个自定义 TextBox 控件,它只允许用户输入数字字符:
注意 为了运行这段代码,你只需在“Windows 应用程序”模板下新建一个 Visual Basic .NET 项目,然后就能在 IDE 自动生成的空白窗体中试验新控件了。在项目中新建一个类 NumericTextBox ,用下面的代码替换 NumericTextBox 类文件的内容,编译该项目。最后,在菜单中选择工具->自定义工具箱,选中先前编译项目得到的 .exe 文件,就能把新控件添加到工具箱了。
Public Class NumericTextBox
Inherits System.Windows.Forms.TextBox
Protected Overrides Sub OnKeyPress(ByVal e As _
System.Windows.Forms.KeyPressEventArgs)
If Not Char.IsDigit(e.KeyChar) Then
e.Handled = True
Else
e.Handled = False
End If
End Sub
End Class
对本例来说,以上代码已经足够了。如果你还觉得它不够完善的话,请改用下列代码,它运用一种奇妙的布尔逻辑减少了代码行数:
Public Class NumericTextBox
Inherits System.Windows.Forms.TextBox
Protected Overrides Sub OnKeyPress(ByVal e As _
System.Windows.Forms.KeyPressEventArgs)
e.Handled = Not Char.IsDigit(e.KeyChar)
End Sub
End Class
现在,你的新控件已经正确显示在窗体中了。它象 TextBox 一样处理事件,并且拥有与 TextBox 一样的方法、属性。你甚至不需更多的编码就能实现对新控件的数据绑定,因为这也是基类控件 TextBox 的功能性之一。
注意 本控件对用户输入的要求十分苛刻:它只允许输入 0 至 9 的数字,也就是说,数字中的逗号、小数点甚至负号都是非法字符。我将在下一篇文章中介绍一个功能更强的输入验证程序。
在 Visual Basic 6.0 中设计本控件时,核心代码会和本范例一样长,可是用于处理控件的 resizing 事件和实现 TextBox 组件属性的代码也会有这么长。由此可见,.NET 提供的继承性能够大大精简源代码。单凭这一点,.NET 就已经令人叹服了,何况它还有许多其它优越性。更奇妙的是,凡是要求使用某一控件的地方,都能改用继承该控件而来的新控件。例如:在任何例程中要求 TextBox 的地方都能用你的 NumericTextBox 控件。不仅如此,从现有控件,而不是从 UserControl 类继承而来的新控件,不但具备基类控件的所有功能性,还能象基类控件一样使用继承得到的属性、方法和事件。因此,任何程序员只要学过标准的 TextBox 控件,就知道如何使用 NumericTextBox 控件。允许继承现有的类/控件,是从Visual Basic 6.0 到 .NET 的一个重大飞跃,可是 .NET 的优点又何止于此!只要你认真学习本系列文章所提供的范例,你就会发现,在 .NET 环境下Windows 窗体控件不但拥有不少强大的功能,而且它们的创建也比在老版本 Visual Basic 中容易得多。
总结
无论对于个人、开发小组,还是软件开发商,自定义控件的开发都是功能最强大的组件技术之一。众所周知,把用户界面与功能性以软件包的形式进行发布,始终是Visual Basic 等可视化开发工具立于不败之地的重要因素;在新版的可视化工具里仍然延续着该理念。到了 .NET 时代,控件的开发思路已经大不相同了,比如:它引入了继承等新特性。好在这些新特性都是对程序员有利的,因为它们有效地减少了人们的编码负担,进而简化了自定义控件的创建过程。本文以若干控件为例向读者介绍了自定义控件的创建过程,以及控件开发中的某些重要技术。当然,我还希望读者能从这些范例中学会如何创建自己的新控件。
本栏文章均来自于互联网,版权归原作者和各发布网站所有,本站收集这些文章仅供学习参考之用。任何人都不能将这些文章用于商业或者其他目的。( Pfan.cn )
【编程爱好者论坛】