探讨.NET 2.0定制控件和用户控件开发

简介:
一、引言
伴 随Visual Studio 2005新加入大量的控件(或增强控件),Windows表单应用程序的开发越来越简单。但是,有些情况下,你仍然需要使用更新的功能来进一步增强现有控 件—很典型的一个例子就是TextBox控件。我们知道,TextBox控件允许用户把数据输入到应用程序中,但是自身并没有提供任何类型的校验(例如校 验输入的数据是一个电子邮件地址)和输入过滤(例如限制输入内容仅为数字型)功能。为此,你需要对每一个TextBox控件编写自己的定制校验和过滤逻 辑。显然,当需要在工程中大量地应用这种控件时,需要相当多的重复工作量。
解决这类问题的一种较好的方案就是对内置控件进行扩展—一旦创建这样的增强控件,那么它们的使用思路同原先基本一样,却“悄悄”地加入了我们需要的增强功能。
在本文中,我将使用一个具体的示例—TextBox控件—向你展示如何扩展Windows表单控件。通过扩展TextBox控件,它将实现如下功能:
◆通过过滤限制数据输入—你可以指定用户输入的有效数据类型(例如数字型,字符型或数字字母的组合)。
◆控制大小写—你可以指定输入数据的大小写形式。
◆ 对显示精度进行格式化—你可以指定输入的数字以一位,两位或三位小数形式显示。
◆定制校验—你可以使用正则表达式执行定制校验(例如校验社会安全保险号,邮政编码或电子邮件地址)。
在本文后面部分,我们将讨论如何把多个Windows表单控件结合成单个控件(也即是一个“用户控件”)的问题。
【注】本文源码的调试环境为:Windows XP Professional+Visual Basic.NET 2005。
 
二、扩展Windows表单控件
 
启 动Visual Studio 2005,创建一个新的Windows应用程序并命名为WindowsControls。在本示例中,这个Windows应用程序工程将用作后面我们将扩 展的控件的宿主。现在,在当前方案中添加一个新的类库工程(文件|添加|新建项目…),并命名此工程为EnhancedTextBox。
 
为了在这个工程中扩展一个Windows表单控件,我们需要添加对System.Windows.Forms命名空间的引用(右击“解决方案资源管理器”中的EnhancedTextBox工程名,然后选择“添加引用…”)。随后,导入下列命名空间:
 
Imports System.Windows.Forms
Imports System.ComponentModel
 
接下来,我们定义几个枚举类型,以用于控制大小写,输入内容过滤以及不同的显示精度:
 
Imports System.Windows.Forms
Imports System.ComponentModel
Public Enum Cases
Mixed
UpperOnly
LowerOnly
End Enum
Public Enum FilterTypes
None
IntegerNumeric
DecimalNumeric
AlphabetsOnly
Alphanumeric
End Enum
Public Enum Precisions
None
OneDecimal
TwoDecimal
ThreeDecimal
End Enum
 
因为我们是在扩展 TextBox 控件,所以必须从 System.Windows.Forms.TextBox 下派生 EnhancedTextBox 类:
 
 
    
Public Class EnhancedTextBox
Inherits System.Windows.Forms.TextBox
'---接下来声明成员变量……
 
现在,我们可以声明成员变量以便存储过滤类型,控制大小写,精度,以及用户设置的正则表达式。此外,我们还要定义一个事件,以便当校验失败时,控件用户能够恰当地做出响应:
 
 _
Public Class EnhancedTextBox
Inherits System.Windows.Forms.TextBox
'---实现过滤的成员变量---
Private _filterType As FilterTypes = FilterTypes.None
Private _casing As Cases = Cases.Mixed
Private _precision As Precisions = Precisions.None
'---用于实现正则表达式校验的成员变量---
Private _regex As StringPrivate _regexErrMsg As String
'---处理错误的事件处理器---

DescriptionAttribute("错误事件处理器。")> _
Public Event Err(ByVal str As String) ("custom>("err")>
注意,在上面的代码中,我使用一个DefaultEvent属性把Err标识为缺省的出错事件处理器。而且,我还使用Category属性来标识 Err事件,从而把它与属性窗口中的定制属性加以关联(后面,我们将详细讨论之)。另外,DescriptionAttribute指出针对这个事件的描 述信息。
三、声明属性
接下来,我们需要声明各种属性以允许用户控制过滤、精度的类型,并能控制输入内容的大小以及用于定制校验的正则表达式输入。
首先,Precision属性用于指定十进制小数位数—用于控制输入文本的格式:
 
    
'---用于设置精度的属性---

    DescriptionAttribute("十进制小数位数。")> _
Public Property Precision() As Precisions
Get
Return _precision
End Get
Set(ByVal value As Precisions)
_precision = value
End Set
End Property("custom>
接下来, EnforceCasing 属性用于指示是否输入的字符应该自动地被转换成大写或小写:
 
 
     
'---用于控制大小写的属性---

    DescriptionAttribute("指示是否输入的字符应该 " & _
"自动地被转换成大写或小写。")> _
Public Property EnforceCasing() As Cases
Get
Return _casing
End Get
Set(ByVal value As Cases)
_casing = value
End Set
End Property("custom>
 
接下来, FilterType 属性用于指定允许输入的数据类型:
 
 
     
'---设置过滤类型的属性---

    DescriptionAttribute("指定要应用的过滤器的类型。")> _
Public Property FilterType() As FilterTypes
Get
Return _filterType
End Get
Set(ByVal value As FilterTypes)
_filterType = value
End Set
End Property("custom>
 
RegularExpression 属性则允许用户设置要使用的正则表达式,以便用于定制校验:
 
 
     
'---用于设置正则表达式的属性---

   DescriptionAttribute("实现定制校验的正则表达式。")> _
Public Property RegularExpression() As String
Get
Return _regex
End Get
Set(ByVal value As String)
_regex = value
End Set
End Property("custom>
 
四、事件编程
最后一步是实现与该增强TextBox控件相关联的各种事件。为了确保用户仅仅可以输入通过我们的过滤器指定的字 符,你可以为该TextBox控件提供相应的KeyPress事件。当输入焦点停在TextBox控件时,无论用户何时按下某个键,都会激活 KeyPress事件。在这个事件中,我们需要实现相应的编程,以便仅允许指定类型的字符显示在本TextBox控件中。该KeyPress事件相应的代 码展示于列表1。
列表1:定制TextBox控件的KeyPress事件编程
 
    
Thread.start { .... }
Private Sub EnhancedTextBox_KeyPress( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.KeyPressEventArgs) _
Handles Me.KeyPress
'---如果没有应用过滤器,则不必作任何进一步处理---
If _filterType = FilterTypes.None Then
Return
End If
'---把字符转换成大/小写---
If _casing = Cases.UpperOnly Then
e.KeyChar = Char.ToUpper(e.KeyChar)
ElseIf _casing = Cases.LowerOnly Then
e.KeyChar = Char.ToLower(e.KeyChar)
End If
'---只允许十进制数字---
If _filterType = FilterTypes.DecimalNumeric Then
'---如果textbox为空并且用户按下某个十进制字符---
If CType(sender, TextBox).Text = String.Empty And e.KeyChar = Chr(46) Then
e.Handled = True
Return
End If
'---如果textbox已经有一个十进制位---
If CType(sender, TextBox).Text.Contains(Chr(46)) And e.KeyChar = Chr(46) Then
e.Handled = True
Return
End If
'---如果用户按键不是一个有效的十进制数---
If (Not (Char.IsDigit(e.KeyChar) Or Char.IsControl(e.KeyChar) Or
(e.KeyChar = Chr(46)))) Then
e.Handled = True
End If
Return
End If
'---只允许整数---
If _filterType = FilterTypes.IntegerNumeric Then
If (Not (Char.IsDigit(e.KeyChar) Or Char.IsControl(e.KeyChar))) Then
e.Handled = True
End If
Return
End If
'---只允许字母(和空格)---
If _filterType = FilterTypes.AlphabetsOnly Then
If (Not (Char.IsLetter(e.KeyChar) Or (e.KeyChar = Chr(32)) Or
Char.IsControl(e.KeyChar))) Then
e.Handled = True
End If
Return
End If
'---只允许字母与数字类型---
If _filterType = FilterTypes.Alphanumeric Then
If (Not (Char.IsLetter(e.KeyChar) Or Char.IsDigit(e.KeyChar) Or (e.KeyChar =
Chr(32)) Or Char.IsControl(e.KeyChar))) Then
e.Handled = True
End If
Return
End If
End Sub
在此,你需要对用户可能输入的各种字符类型进行检查。如果检测到一个无效字符,则把e.Handled属性设置为 True;于是,此字符将不会被添加到该TextBox中。IsControl()方法负责确定是否一个输入的字符是一个控制字符(例如Tab键,回车 键,退格删除键—BackSpace,等等)。在此,进行控制字符检查是很重要的,为什么呢?以BackSpace字符为例;如果在KeyPress事件 中忽略检测BackSpace字符,那么,用户以后可能无法再使用BackSpace键来删除一个输入到该TextBox控件中的字符—这显然与我们的意 愿相违背。
在通过KeyPress事件实现禁止用户输入非法字符的同时,我们还需要在另一个事件—LostFocus事件—中进行一些编程。当输入焦点离开该TextBox控件时将激活这个LostFocus事件—例如按下Tab键导航到下一个控件。
在这个事件中,你需要执行的一个很重要的任务是校验TextBox控件的内容。这是因为无效的字符仍然可能会出现 在该控件中—可能通过其它非直接键盘输入的方法输入;用户可以以编程方式给该TextBox控件赋值,或者把无效的字符直接粘贴(Ctrl-V)到这个 TextBox控件中。例如,如果你使用IntegerNumeric过滤来设置一个TextBox控件,那么,你仍然能通过编程给它赋一个十进制数字 值。在本文示例中,KeyPress事件不能用于限制赋值。因此,在LostFocus事件中,你需要执行各种类型的校验以确保TextBox控件中的值 与指定的过滤相一致。篇幅所限,LostFocus事件相应的代码在此省略,有兴趣的读者可参考本文下载源码。
注意,在本示例中,我们还创建了一个ShowErrorBalloon()方法,用于通过一个汽球小窗(使用ToolTip控件)更为友好地显示一条消息:
 
     
Thread.start { .... }
Public Sub ShowErrorBalloon(ByVal str As String)
Dim toolTip1 As New ToolTip
With toolTip1
.IsBalloon = True
.Show(str, Me,10, -40, 2000)
End With
End Sub
五、测试扩展的TextBox控件
现在,增强控件构建完毕,让我们开始测试这个扩展的TextBox控件。为此,右击“解决方案资源管理器”中的EnhancedTextBox工程并选择“生成”把该工程编译成一个DLL文件。
再回到WindowsControls工程,右击工具箱中的“公共控件”选项卡并选择“选择项…”。
在“选择工具箱项”窗口中,点击“浏览…”按钮并导航到工程的bin\Debug文件夹,然后选择EnhancedTextBox.dll(见图1),并点击“打开”按钮。
 
图1.选择EnhancedTextBox.dll
现在,该EnhancedTextBox控件会出现在工具箱中。于是,我们可以把它的一个副本拖动到WindowsControls工程的缺省的Form1表单中(见图2)。
 
图2.把EnhancedTextBox控件拖动到表单Form1
右击新添加的EnhancedTextBox控件并且选择“属性”。现在,我们会看到这个控件的属性窗口。如果你切换到“分类”视图,你会发现它们位于“Custom Properties”分类下(见图3)。现在,你可以使用属性窗口设置各种定制属性了。
 
图3.定制EnhancedTextBox控件的属性和事件
作为选择,你还可以以编程方式设置属性,如下所示:
 
    
'---第一个例子---
With EnhancedTextBox1
.FilterType = EnhancedTextBox.FilterTypes.DecimalNumeric
.Precision = EnhancedTextBox.Precisions.TwoDecimal
End With
'---第二个例子---
With EnhancedTextBox1
.RegularExpression = _
"^[\w-\.]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]{2,3}$"
.RegularExpressionErrorMessage = _
"电子邮件地址不正确"
End With
为了更为方便地测试新的扩展控件,我在表单上添加了一些控件(见图4)。为了更为直观地说明问题,这个表单还展示 各种过滤器之间的逻辑关系。例如,如果你选择DecimalNumeric过滤,那么,仅应用精度格式控制;而如果你选择AlphabetsOnly或 Alphanumeric过滤器,那么,仅应用大小写格式控制。如果输入一个正则表达式,那么,在控制失去焦点后将校验输入的文本。
 
图4.用于测试EnhancedTextBox控件的屏幕
 
图5.使用汽球窗口显示用户校验失败
如果输入的文本没有通过校验,将显示一个汽球窗口(见图5)。
六、开发用户控件
前面,你学习了如何使用各种过滤器和格式化来扩展VS2005内置的TextBox控件。如今,你可以很容易地使用新的增强的TextBox控件来执行各种类型的输入过滤,而不必再担心其它细节问题了。
除了能够扩展单个的Windows表单控件之外,我们还可以把它们组合成一个单一的控件—这可以通过使用 Windows控件库的工程类型来实现。借助于前面创建的EnhancedTextBox控件,我们可能想把许多这样的控件组合到一起而形成一个新的控件 (称为“用户控件”),例如,一个包含两个TextBox控件的控件,从而允许用户成组地输入自己的名字和电子邮件地址。现在,让我们进行这样的试验。
还是使用前面的方案,但是,要把一个新的“Windows控件库”工程添加到当前方案中(文件|添加|新建项 目…,在随后弹出的“添加新项目”对话框中选择“Windows控件库”模板),然后把这个新工程命名为WindowsControlLibrary1。 通过图6你可以看到该控件的缺省设计布局。
 
图6.用户控件设计视图
 
图7.使用一个标签和增强的TextBox控件填充用户控件
然后,按图7所示,在屏幕上添加两对Label和EnhancedTextBox,并按表格1所示设置各个控件的属性。
表格1. 组成用户控件的各种子控件的属性值。
控件

属性

UserControl1

BorderStyle

FixedSingle.

txtE_Name

FilterType

AlphabetsOnly

txtE_Email

RegularExpression

"^[\w-\.]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]{2,3}$"

txtE_Email

RegularExpressionErrorMessage

“电子邮件格式不正确。”

双击该用户控件,切换到其code-behind代码部分。在此,你将添加两个成员变量和两个属性以允许用户设置用户名和电子邮件地址:
 
     
Public Class UserControl1
Private _Name As String
Private _Email As String
Public Property UserName() As String
Get
Return txtE_Name.Text
End Get
Set(ByVal value As String)
txtE_Name.Text = value
End Set
End Property
Public Property Email() As String
Get
Return txtE_Email.Text
End Get
Set(ByVal value As String)
txtE_Email.Text = value
End Set
End Property
 
然后,在该用户控件的设计视图中,双击txtE_Email控件,系统将为我们自动创建Err事件处理器:
 
 
     
Private Sub txtE_Email_Err(ByVal str As System.String) _
Handles txtE_Email.Err
With txtE_Email
.BackColor = Color.LightYellow
End With
End Sub
 
无论何时存在一个校验错误,这个事件都会激活。在本例中,在校验失败时,该控件的背景色将被设置为浅黄色。
 
七、测试用户控件
 
现在,我们开始测试这个新的用户控件。右击“解决方案资源管理器”中的UserControl工程并选择“生成”从而把该工程编译成一个DLL。
 
返回到WindowsControls工程,右击工具箱中的“公共控件”选项卡并选择“选择项…”。在“选择工具箱 项”窗口,点击“浏览…”按钮,导航到工程的bin\Debug文件夹,并选择WindowsControlLibrary1.dll;最后点击“打 开”。现在,用户控件应该出现在工具箱中。最后,把它拖动到表单Form1的底部(见图8)。
 
 
 
图8.应用中的新用户控件
 
 
 
图9.通过一个黄色小气球中显示错误消息来通知用户输入校验失败
 
试着在第一个控件中输入你的名字,再按Tab键转到下一个控件。如果你输入一个无效电子邮件地址,然后按Tab键使其失去焦点,这时将出现一个汽球,并且其背景色改变为浅黄色(见图9)。
 
八、小结
 
在本文中,我们通过一个简单但较全面的例子探讨了如何扩展Windows表单控件并且把它们打包为新的用户控件的过程。在实际开发中,特别是在工程中反复应用到某些相同功能的控件时进行这样的扩展显然能极大地提高生产效率。

















本文转自朱先忠老师51CTO博客,原文链接: http://blog.51cto.com/zhuxianzhong/59826 ,如需转载请自行联系原作者



相关文章
|
13天前
|
人工智能 量子技术 C#
【专栏】.NET 开发:开启数字化新时代
【4月更文挑战第29天】.NET开发在数字化新时代中发挥关键作用,借助跨平台能力、高性能和现代编程语言支持,如C#,助力企业实现数字化转型。通过企业级应用开发、移动应用和云计算集成,.NET加速业务流程和提升用户体验。未来,.NET将涉足AI、ML、MR/AR及量子计算,持续推动技术创新和数字化转型。开发者应提升技能,适应高性能需求,把握发展机遇。
|
13天前
|
缓存 监控 算法
【专栏】.NET 开发:实现卓越性能的途径
【4月更文挑战第29天】本文探讨了.NET开发中的性能优化,强调了理解性能问题根源和使用分析工具的重要性。基础优化包括代码优化(如减少计算、避免内存泄漏)、资源管理及选择合适算法。高级策略涉及并行编程、缓存策略、预编译(AOT)和微服务架构。持续性能测试与监控是关键,包括性能测试、监控分析和建立优化反馈循环。开发者应持续学习和实践性能优化,以构建高性能应用。
|
13天前
|
开发框架 .NET C#
【专栏】理解.NET 技术,提升开发水平
【4月更文挑战第29天】本文介绍了.NET技术的核心概念和应用,包括其跨平台能力、性能优化、现代编程语言支持及Web开发等特性。文章强调了深入学习.NET技术、关注社区动态、实践经验及学习现代编程理念对提升开发水平的重要性。通过这些,开发者能更好地利用.NET构建高效、可维护的多平台应用。
|
13天前
|
机器学习/深度学习 vr&ar 开发者
【专栏】.NET 技术:引领开发新方向
【4月更文挑战第29天】本文探讨了.NET技术如何引领软件开发新方向,主要体现在三方面:1) 作为跨平台开发的先锋,.NET Core支持多操作系统和移动设备,借助.NET MAUI创建统一UI,适应物联网需求;2) 提升性能和开发者生产力,采用先进技术和优化策略,同时更新C#语言特性,提高代码效率和可维护性;3) 支持现代化应用架构,包括微服务、容器化,集成Kubernetes和ASP.NET Core,保障安全性。此外,.NET还不断探索AI、ML和AR/VR技术,为软件开发带来更多创新可能。
|
13天前
|
物联网 vr&ar 开发者
【专栏】.NET 技术:为开发注入活力
【4月更文挑战第29天】本文探讨了.NET技术的创新,主要体现在三个方面:1) .NET Core实现跨平台开发革命,支持多种操作系统和硬件,如.NET MAUI用于多平台UI;2) 性能提升与生产力飞跃,C#新特性简化编程,JIT和AOT优化提升性能,Roslyn提供代码分析工具;3) 引领现代化应用架构,支持微服务、容器化,内置安全机制。未来,.NET 7将带来更多新特性和前沿技术整合,如量子计算、AI,持续推动软件开发创新。开发者掌握.NET技术将赢得竞争优势。
|
13天前
|
人工智能 前端开发 Cloud Native
【专栏】洞察.NET 技术的开发趋势
【4月更文挑战第29天】本文探讨了.NET技术的三大发展趋势:1) 跨平台与云原生技术融合,通过.NET Core支持轻量级、高性能应用,适应云计算和微服务;2) 人工智能与机器学习的集成,如ML.NET框架,使开发者能用C#构建AI模型;3) 引入现代化前端开发技术,如Blazor,实现前后端一致性。随着.NET 8等新版本的发布,期待更多创新技术如量子计算、AR/VR的融合,.NET将持续推动软件开发的创新与进步。
|
13天前
|
开发框架 物联网 测试技术
【专栏】.NET 开发:打造领先应用的基石
【4月更文挑战第29天】本文探讨了.NET开发框架为何成为构建领先应用的首选。高性能与稳定性是.NET的核心优势,它采用先进的技术和优化策略,如.NET Core的轻量级设计和JIT/AOT编译模式。跨平台兼容性让开发者能用相同代码库在不同操作系统上构建应用。现代化的开发体验,如C#语言的创新特性和Visual Studio的强大工具,提升了开发者生产力。丰富的生态系统和广泛支持,包括庞大的开发者社区和微软的持续投入,为.NET提供了坚实后盾。
|
13天前
|
人工智能 前端开发 Devops
【专栏】洞察.NET 技术在现代开发中的作用
【4月更文挑战第29天】本文探讨了.NET技术在现代软件开发中的核心价值、应用及挑战。.NET提供语言统一性与多样性,强大的Visual Studio工具,丰富的类库,跨平台能力及活跃的开发者社区。实际应用包括企业级应用、Web、移动、云服务和游戏开发。未来面临性能优化、容器化、AI集成等挑战,需持续创新。开发者应深入理解.NET,把握技术趋势,参与社区,共创美好未来。
|
13天前
|
机器学习/深度学习 人工智能 开发者
【专栏】.NET 技术:为开发带来新机遇
【4月更文挑战第29天】本文探讨了.NET技术如何为软件开发带来新机遇,分为三个部分:首先,.NET的跨平台革命,包括.NET Core的兴起、Xamarin与.NET MAUI的移动应用开发、开源社区的推动及性能优化;其次,介绍了云服务与微服务架构的集成,如Azure云服务、微服务支持、DevOps与CI/CD,以及Docker容器化;最后,讨论了AI与机器学习集成,如ML.NET、认知服务、TensorFlow和ONNX,使开发者能构建智能应用。面对这些机遇,开发者应不断学习和适应新技术,以创造更多价值。
|
13天前
|
算法 Java 编译器
【专栏】.NET 开发:实现高效能的秘诀
【4月更文挑战第29天】本文探讨了提升.NET应用性能的三个方面:理解.NET运行时(垃圾回收、JIT编译器、异步编程和线程并发)、优化代码与算法(代码细节、数据结构选择和算法效率)以及利用工具和框架(性能分析工具、高性能库和CI/CD流程)。通过深入学习、合理设计和有效工具,开发者可实现.NET应用的高效能。