发布日期: 6/24/2004 | 更新日期: 6/24/2004

Billy Hollis

摘要:Billy Hollis 说明了如何生成单个 Windows 窗体控件,以包装全套 ASP.NET 验证程序的大多数功能以及其他一些自定义功能。

本页内容

扩展程序提供程序扩展程序提供程序

创建您自己的扩展程序提供程序创建您自己的扩展程序提供程序

在项目中使用扩展程序提供程序在项目中使用扩展程序提供程序

小结小结

下载 vbnet04082003_sample.exe 示例文件

我已经毫不掩饰地表示过,我更喜欢智能客户端,而不是基于浏览器的应用程序。但这并不意味着我对好主意视而不见。ASP.NET 小组已经做了一些了不起的工作。其中一个很棒的例子是可用于 Web 窗体的验证程序控件。只须拖放这些控件,转眼之间就已经在文本框中内置了数据验证功能。这个主意是如此高明,以至于我们中的一些人已经决定将它运用到 Windows 窗体中。

实际上,借助于 Windows 窗体的一些高级功能,甚至可以使拖放验证程序变得更为强大。本文说明了如何生成单个 Windows 窗体控件,以包装全套 ASP.NET 验证程序的大多数功能以及其他一些自定义功能,包括:

•每当用户进行击键操作时都执行数据验证。

•检查数据类型。

•多种通知用户数据无效的方法。

我不是第一个想到这个主意的人。当我看到 Francesco Balena 撰写的题为 Use Declarative Field Validation 的文章时,我已经开发了自己的验证程序的简单版本。Francesco 的文章写很好,但由于是以印刷形式发表,所以他不得不减小它的篇幅。

本文将重复 Balena 先生表达的一些观点,但会更详细地阐述。我还一直在产品项目中使用验证程序,所以我收录了许多新的功能,这些功能使验证程序概念更加适合于产品应用程序。事实上,本文中验证程序的某个版本已经应用于我现在参与的一个主要项目之中。

扩展程序提供程序

要了解 Windows 窗体验证程序的工作方式,有必要了解扩展程序提供程序控件。它们是 Windows 窗体中的新控件,与 Microsoft Visual Basic? 6.0 或更低版本中的任何控件都没有相似之处。该名称来源于以下事实:这些控件通过为现有的可视控件提供 新的属性,扩展 了这些控件。

了解这一事实的最简便方法是使用 Windows 窗体中内置的某个扩展程序提供程序。Tooltip 控件即为一个示例。如果您的窗体上没有 Tooltip 控件,则任何被拖动到该窗体上的控件都不会具有 Tooltip 属性。然而,如果以后将 Tooltip 控件拖动到该窗体上(从而使其出现在组件栏中),则所有可视控件突然间都将获得 Tooltip 属性。 1 显示了文本框的 Properties 窗口在添加 Tooltip 扩展程序提供程序之前和之后所呈现的外观。请注意,添加了“Tooltip on Tooltip1”属性。

vbnet04082003-fig01

1. 文本框控件的 Property 窗口在添加工具提示扩展程序提供程序之前和之后所呈现的外观(左侧为添加之前的外观,右侧为添加之后的外观)。工具提示控件使所有控件都具有了名为“Tooltip on Tooltip1”的新属性。

当然,您可以在 Properties 窗口中设置工具提示的值。当鼠标停留在 TextBox1 控件上方时,在 Properties 窗口中输入的字符串值将作为工具提示显示出来。

还可以在代码中设置新的 Tooltip 属性,但语法与期望的不同。因为扩展程序提供程序实际管理该属性,所以您必须在代码中调用 Tooltip1 扩展程序提供程序控件上的方法来获取或设置属性值。以下代码说明如何使用 Tooltip1 扩展程序提供程序来获取和设置 TextBox1 的 Tooltip 属性:

’ Fetch the tooltip for TextBox1
Dim sTooltip As String
sTooltip = ToolTip1.GetToolTip(TextBox1)
‘ Append a period to the tooltip and put it back.
sTooltip &= “.”
ToolTip1.SetToolTip(TextBox1, sTooltip)

在 Windows 窗体中还有其他两个内置的扩展程序提供程序控件:ErrorProviderHelpProvider。本文稍后将使用 ErrorProvider 控件。它提供了一种以可视形式向用户指出问题(如数据验证错误)的方法。

ErrorProvider 所提供的最重要属性名为 Error。假设我将一个 ErrorProvider 控件拖动到窗体上,该窗体中有一个名为 TextBox1 的文本框,然后我将 TextBox1 的 Error 属性设置为 Data error in TextBox1。当窗体加载时,ErrorProvider 控件将在 TextBox1 旁边显示一个图标,以表明它具有错误。 2 显示了该窗体的工作方式。

vbnet04082003-fig02

2. 包含 TextBox ErrorProvider 的窗体。TextBox1 Error 属性已经设置,从而使 ErrorProvider 显示红色图标。

如果鼠标停留在该红色图标上方,则会显示工具提示,其中含有错误消息 Data error in TextBox1。如果以后在代码中将 TextBox1 的 Error 属性设置为空字符串,TextBox1 旁边的红色图标将消失。采用同样的技术设置或清除 Error on ErrorProvider1 属性,能够在窗体中任何控件的旁边显示一个图标。

ErrorProvider 所提供的其他属性包括:IconAlignment,它确定错误图标的显示位置与控件之间的相对关系;IconPadding,它确定控件与错误图标之间的距离。

ErrorProvider 控件的属性

到目前为止,我们已经看到了 ErrorProvider 为其他控件提供 的属性。然而,作为一个组件,ErrorProvider 本身就具有属性。如果您突出显示 ErrorProvider 控件,Properties 窗口将如 3 所示。

vbnet04082003-fig03

3. ErrorProvider 所具有的与 ErrorProvider 组件相关联的属性,该属性现在与一个特定控件相关联。

ErrorProvider 本身的属性会影响所有与 ErrorProvider 交互的控件。例如,如果为 ErrorProvider 设置 Icon 属性,将会更改用于所有具有非空 Error on ErrorProvider1 属性的控件的图标。

当我们开始编写自己的扩展程序提供程序控件时,需要记住扩展程序提供程序本身的属性及其提供给其他控件的属性之间的区别,因为它们在代码中的实现方式是不同的。

返回页首返回页首

创建您自己的扩展程序提供程序

我尚未见到过许多自定义扩展程序提供程序控件的示例,但编写一个这样的控件并不是很困难。.NET 框架提供了一个要求扩展程序提供程序必须实现的接口,同时还需要遵循某些约定以使控件能够与 Windows 窗体中的可视化设计器协调工作。我们将通过创建验证程序控件(该控件名为 MSDNTextBoxValidator)观察一下生成扩展程序提供程序的过程。但首先,我们必须阐明该控件的设计。

下面列出了验证程序将为文本框提供的功能:

Required 检查:检测是否起码在文本框中输入了一个值。

Range 检查:验证文本框中输入的值,以确保它处于由最小值和最大值定义的范围之内。

DataType 检查:检查文本框中的数据是否可以转换为需要的数据类型。

RegularExpression 检查:根据正则表达式对文本框中的字符串进行验证。

上述每一项验证都需要在违反有效性时提供适当的消息。不过,我们需要使该消息便于用户理解,所以有必要为将在控件消息中使用的控件指定一个友好的名称。

检测并显示验证错误

要在用户键入时提供反馈,对特定文本框进行的验证需要在产生击键操作时立即发生,并且需要在用户将焦点从文本框移走时发生。

显示验证错误的最常见方法是使用 ErrorProvider,所以需要我们的验证程序与 ErrorProvider 协调工作以进行显示。提供一个验证摘要也很有用,该摘要是一个列表,其中列出了窗体中所有控件的当前验证错误消息。理想情况下,该列表需要按照控件的 TabOrder 对消息进行排序。

我们需要哪些属性?

正如我们在前面所看到的,对于扩展程序提供程序,有两类属性:一类属性是提供给其他控件的属性,一类属性是扩展程序提供程序本身的属性。

要满足上述要求,需要为每个文本框提供下列属性:

属性
定义

DataType

一个枚举值,可以设置为下列类型之一:

StringType(此为默认类型)

ByteType

Int16Type

Int32Type

Int64Type

SingleType

DoubleType

DecimalType

DateTimeType

DisplayName

要在错误信息中使用的控件的用户友好名称。如果为空,将使用控件名。

MinValue

验证范围的最小值。(可选)

MaxValue

验证范围的最大值。(可选)

Required

布尔型属性,表示该文本框是否必须含有数据。默认值为 False。

RegularExpression

正则表达式,用于验证文本框中的数据。可以使用正则表达式根据电话号码和社会保障号码之类的格式进行验证。(如果您不太熟悉正则表达式,在 MSDN 上有许多讨论它们的文章可供参阅)。

为使 ErrorProvider 对窗体中的所有文本框都有效,使用验证程序控件本身的属性对其进行了设置。使用检测到的所有验证错误来设置那些使用该 ErrorProvider 控件的文本框的 Error 属性。

尽管 ErrorProvider 属性是可选的,您通常需要对其进行设置。否则,将没有任何直观迹象表明已经检测出验证错误。

控件的产品版还具有一些附加属性。例如,产品版可以改变含有无效数据的文本框的背景色。在本文中我不再讨论这些高级功能,但供下载的资料中既包含简单版本(如本文所述),又包含具有附加功能的高级版本。

使用方法

本文的下载资料中包含完整的 MSDNTextboxValidator,所以如果您愿意,可以直接安装并开始使用它。您需要将其添加到工具箱(稍后我们将对其进行讨论)中。但如果您对构建细节感兴趣,或者您希望了解更多有关扩展程序提供程序控件工作方式的内容,可使用以下循序渐进的过程,从头开始生成简单版本的 MSDNTextBoxValidator

1.

在 Visual Studio? 中新建一个 Class library 类型的项目。将其命名为 MSDNValidators

2.

在该新项目中,选择 Projects | Add Component。将该组件命名为 MSDNTextboxValidator。从项目中删除默认的 Class1。

3.

因为我们将使用控件,所以需要对 Windows 窗体 DLL 的引用。选择 Project | Add Reference,然后向 System.Windows.Form.DLL 中添加一个引用。

4.

我们需要导入几个命名空间,以使代码能够正常工作。在刚刚创建的新组件代码的顶部放置以下代码行:

Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Drawing

5.

需要使用扩展程序提供程序来实现名为 iExtenderProvider 的接口。将以下代码行恰好放在内容为 Inherits System.ComponentModel.Component 的行的后面:

Implements iExtenderProvider

6.

iExtenderProvider 接口具有一个名为 CanExtend 的方法。该方法返回一个 Boolean 类型的值,该值表明控件是否可以使用该特定扩展程序提供程序进行扩展。因为我们的验证程序只适用于文本框,所以该方法需要对文本框返回 True,而对其他任何控件返回 False。将以下代码添加到组件中,以实现 CanExtend 方法:

Public Function CanExtend(ByVal extendee As Object) As Boolean _
          Implements IExtenderProvider.CanExtend
    If TypeOf extendee Is TextBox Then
        Return True
    Else
        Return False
    End If
End Function

7.

扩展程序提供程序使用属性来告诉 Visual Studio .NET 它将向受支持的控件提供哪些属性。要包含这些特性,请将以下代码直接放到内容为 Public Class MSDNTextboxValidator 的行的前面:

<ProvideProperty("DataType", GetType(Control)), _ ProvideProperty("DisplayName", GetType(Control)), _ ProvideProperty("MinValue", GetType(Control)), _ ProvideProperty("MaxValue", GetType(Control)), _ ProvideProperty("Required", GetType(Control)), _ ProvideProperty("RegularExpression", GetType(Control))> _

我们将在后面的某个步骤中添加这些属性的代码。

8.

接下来,创建扩展程序提供程序控件本身需要的属性。有时并不存在这样的属性,但我们的验证程序控件具有两个属性,即我们前面所讨论过的 ErrorProviderEnabled。它们的代码一目了然,因此我只是将需要插入的代码列在下面,以便支持前面讨论的属性:

' The ErrorProvider control where error messages can be shown 
' (with icons, etc.)
Dim m_ErrorProvider As ErrorProvider

 _
Public Property ErrorProvider() As ErrorProvider
    Get
        Return m_ErrorProvider
    End Get
    Set(ByVal Value As ErrorProvider)
        m_ErrorProvider = Value
    End Set
End Property

如果您不熟悉为该属性显示的 Description 特性,您只须知道当该属性被选中时,此特性中的文本将显示在 Properties 窗口的底部。当您创建属性时,包含一个将显示在 Properties 窗口中的 Description 属性始终是一个不错的主意。

9.

下一步,我们要创建将提供给其他控件的属性。然而,还必须首先采取一个步骤。我们的扩展程序提供程序负责创建其自身的位置,以便存储它将要提供的属性值。通常通过创建对象集合来完成这项工作,其中,对象与要扩展的控件一一对应。与控件相关联的对象存放该控件的扩展属性值。

在创建该集合之前,我们必须创建将要存储在该集合中的对象的类。为此,请将以下私有类的代码恰好放在内容为 End Class 的行的前面:

Private Class TextboxValidatorProvidedProperties
    Public DataType As DataTypeConstants
    Public RegularExpression As String = String.Empty
    Public MinValue As String = String.Empty
    Public MaxValue As String = String.Empty
    Public Required As Boolean = False
    Public DisplayName As String
End Class

该类是一个嵌套在 MSDNTextboxValidator 类内部的私有类。它是私有类的原因是在 MSDNTextboxValidator 外部根本不需要它。以这种方式使用私有类是一种提高封装性的好做法。

10.

上面的类引用了名为 DataTypeConstants 的枚举。该枚举必须是公用的,因为它必须在 Visual Studio 设计器中可用。相应地,请将该枚举的下列代码行放在代码的最后面(在 MSDNTextboxValidator 的 End Class 行的下面):

Public Enum DataTypeConstants
    StringType
    ByteType
    Int16Type
    Int32Type
    Int64Type
    SingleType
    DoubleType
    DecimalType
    DateTimeType
End Enum

11.

现在,我们已经准备好创建集合来存放所提供的属性值。我们将把该集合声明为 Hashtable,并包含一个函数以便向该集合中添加新控件的对象。请将以下代码添加到步骤 8 中输入的公用属性的下面:

' this hashtable holds property values for individual controls
Friend htProvidedProperties As New Hashtable()

Private Function GetAddControl(ByVal ctrl As Control) As _
  TextboxValidatorProvidedProperties
    If htProvidedProperties.Contains(ctrl) Then
        Return DirectCast(htProvidedProperties(ctrl), _
          TextboxValidatorProvidedProperties)
    Else
        ' add an element to the hashtable
        Dim ProvidedProperties As New TextboxValidatorProvidedProperties()
        htProvidedProperties.Add(ctrl, ProvidedProperties)

        ' Trap the Validating event and KeyUp events
        AddHandler ctrl.Validating, AddressOf ValidatingHandler
        AddHandler ctrl.KeyUp, AddressOf CheckOnKeystrokeHandler
        Return ProvidedProperties
    End If
End Function

除了创建 TextboxValidatorProvidedProperties 类型的新对象并将其添加到哈希表中之外,该函数还会为其属性被托管的文本框动态挂钩两个事件。当该文本框由于某种原因失去焦点时,将激发 Validating 事件;当某个键被按下时,将激发 KeyUp 事件。这两个事件都会导致对文本框中的文本进行验证。

12.

该步骤中的代码将事件挂钩到我们尚未包括的两个例程中。这两个例程的名称为 ValidatingHandlerCheckOnKeystrokeHandler。请将它们的代码直接放在步骤 11 中输入的代码的下面。

    Private Sub ValidatingHandler(ByVal sender As Object, _
                ByVal e As System.ComponentModel.CancelEventArgs)

    ' Get the control to work with and the collection item associated
    ' with the control
    Dim ctrl As Control
    ctrl = CType(sender, Control)
    ProcessError(ctrl)
End Sub

Private Sub CheckOnKeystrokeHandler(ByVal sender As Object, _
            ByVal e As KeyEventArgs)

    ' Get the control to work with and the collection item associated
    ' with the control
    Dim ctrl As Control
    ctrl = CType(sender, Control)
    ProcessError(ctrl)
End Sub

Public Sub ProcessError(ByVal ctrl As Control)
    Dim ctrlProperties As TextboxValidatorProvidedProperties
    ctrlProperties = CType(htProvidedProperties(ctrl), _
                     TextboxValidatorProvidedProperties)

    ' Fetch the error message - it may be empty, which means no error.
    Dim msg As String = GetErrorMessage(ctrl)

    ' Unconditionally set the Error message in the ErrorProvider.
    If Not Me.ErrorProvider Is Nothing Then
        Me.ErrorProvider.SetError(ctrl, msg)
    End If

End Sub

13.

接下来,我们需要包含用于获取和设置所提供属性的方法。所提供属性的值存储在前面创建的哈希表中的对象中。获取和设置例程的命名方式需要遵循特定的约定。对于所提供的名为 NewProperty 的属性,用于获取和设置该属性的方法必须分别名为 GetNewPropertySetNewProperty。获取例程只须知道要为其获取该属性的控件。设置例程还需要知道新的属性值。以下是前面列出的所提供属性的获取例程和设置例程。应该将这些代码恰好添加到上述步骤 12 中代码的下面。

' the DataType property
Function GetDataType(ByVal ctrl As Control) As DataTypeConstants
    If htProvidedProperties.Contains(ctrl) Then
        Return DirectCast(htProvidedProperties(ctrl), _
               TextboxValidatorProvidedProperties).DataType
    Else
        Return DataTypeConstants.StringType
    End If
End Function

Sub SetDataType(ByVal ctrl As Control, ByVal value As _
  DataTypeConstants)
    GetAddControl(ctrl).DataType = value
End Sub

' the RegularExpression property
Function GetRegularExpression(ByVal ctrl As Control) As String
    If htProvidedProperties.Contains(ctrl) Then
        Return DirectCast(htProvidedProperties(ctrl), _
               TextboxValidatorProvidedProperties).RegularExpression
    Else
        Return String.Empty
    End If
End Function

Sub SetRegularExpression(ByVal ctrl As Control, ByVal value As String)
    If value Is Nothing Then value = ""
    GetAddControl(ctrl).RegularExpression = value
End Sub

' the MinValue property
Function GetMinValue(ByVal ctrl As Control) As String
    If htProvidedProperties.Contains(ctrl) Then
        Return DirectCast(htProvidedProperties(ctrl), _
               TextboxValidatorProvidedProperties).MinValue
    Else
        Return String.Empty
    End If
End Function

Sub SetMinValue(ByVal ctrl As Control, ByVal value As String)
    If value Is Nothing Then value = String.Empty
    GetAddControl(ctrl).MinValue = value
End Sub

' the MaxValue property
Function GetMaxValue(ByVal ctrl As Object) As String
    If htProvidedProperties.Contains(ctrl) Then
        Return DirectCast(htProvidedProperties(ctrl), _
               TextboxValidatorProvidedProperties).MaxValue
    Else
        Return String.Empty
    End If
End Function

Sub SetMaxValue(ByVal ctrl As Control, ByVal value As String)
    If value Is Nothing Then value = String.Empty
    GetAddControl(ctrl).MaxValue = value
End Sub

' the Required property
Function GetRequired(ByVal ctrl As Control) As Boolean
    If htProvidedProperties.Contains(ctrl) Then
        Return DirectCast(htProvidedProperties(ctrl), _
               TextboxValidatorProvidedProperties).Required
    Else
        Return False
    End If
End Function

Sub SetRequired(ByVal ctrl As Control, ByVal value As Boolean)
    GetAddControl(ctrl).Required = value
End Sub

' the DisplayName property
Function GetDisplayName(ByVal ctrl As Control) As String
    If htProvidedProperties.Contains(ctrl) Then
        Return DirectCast(htProvidedProperties(ctrl), _
               TextboxValidatorProvidedProperties).DisplayName
    Else
        Return String.Empty
    End If
End Function

Sub SetDisplayName(ByVal ctrl As Control, ByVal value As String)
    If value Is Nothing Then value = String.Empty
    GetAddControl(ctrl).DisplayName = value
End Sub

14.

现在我们已经做好准备,以便编写用于执行实际验证的函数。该函数名为 GetErrorMessage,并且从在步骤 12 中输入到代码中的 ProcessError 例程中调用。以下即为该函数,应该将其恰好输入到上述步骤 13 中的代码后面:

Public Function GetErrorMessage(ByVal ctrl As Control) As String
    ' This array is used to speed up validation against the _
    ' DataType property.
    ' IMPORTANT: its elements must match the ordering of _
      DataTypeConstants
    ' in the enum defined at the end of this module.
    Static types() As Type = {GetType(String), GetType(Byte), _
      GetType(Int16), GetType(Int32), GetType(Int64), _
      GetType(Single), _
      GetType(Double), GetType(Decimal), GetType(DateTime)}

    ' Get the set of provided properties for this control.
    Dim ProvidedProperties As TextboxValidatorProvidedProperties = _
        DirectCast(htProvidedProperties(ctrl), _
        TextboxValidatorProvidedProperties)

    ' Use either the DisplayName value or the control's name for _
       messages
    Dim sDisplayName As String
    If ProvidedProperties.DisplayName = String.Empty Then
        sDisplayName = ctrl.Name
    Else
        sDisplayName = ProvidedProperties.DisplayName
    End If

    Dim value As String = ctrl.Text

    ' If Required is true and TextBox is empty, 
    ' then return validation error. If not required,
    ' it's OK to be empty, and don't check anything else.
    If value.Length = 0 Then
        If ProvidedProperties.Required Then
            Return sDisplayName & " is required."
        Else
            ' if not required, return OK
            Return String.Empty
        End If
    End If

    Dim valueType As Type = types(CInt(ProvidedProperties.DataType))

    ' Validate against the DataType property.
    Try
        'Attempt the conversion, return False if any exception.
        Dim o As Object = Convert.ChangeType(value, valueType)

        ' Additional validation for integer types. 
        Select Case ProvidedProperties.DataType
            Case DataTypeConstants.ByteType, _
                 DataTypeConstants.Int16Type, _
                 DataTypeConstants.Int32Type, _
                 DataTypeConstants.Int64Type
                If CDec(value) <> CLng(value) Then
                    Return "**No fractional part is allowed in textbox " _
                            & sDisplayName & "**"
                End If
        End Select

    Catch ex As Exception
        Return "**Entry in textbox " & sDisplayName & _
               " can't be converted to type " & valueType.Name & "**"
    End Try

    ' Validate against the RegularExpression property
    If ProvidedProperties.RegularExpression.Length > 0 Then
        ' Must enclose the regular expression between ^ and $
        If Not System.Text.RegularExpressions.Regex.IsMatch(value, _
               "^" & _
               ProvidedProperties.RegularExpression & "$") Then
            Return "**Entry in textbox " & sDisplayName & _
                   " doesn't match expected format**"
        End If
    End If

    ' validate against the MinValue and MaxValue property
    If ProvidedProperties.MinValue.Length > 0 Then
        ' perform type-agnostic comparison
        If Convert.ChangeType(value, valueType) < _
           Convert.ChangeType(ProvidedProperties.MinValue, valueType) Then
            Return "**Entry in textbox " & sDisplayName & _
                   " is too low**"
        End If
    End If
    If ProvidedProperties.MaxValue.Length > 0 Then
        ' perform type-agnostic comparison
        If Convert.ChangeType(value, valueType) > _
           Convert.ChangeType(ProvidedProperties.MaxValue, valueType) Then
            Return sDisplayName & " is greater than the maximum value _
              of " & ProvidedProperties.MaxValue
        End If
    End If

    ' all tests passed
    Return String.Empty
End Function

15.

最后,我们需要准备 ValidationSummary,它将显示当前所有无效消息的列表。以下为相应的代码,应该将其恰好放在步骤 14 的代码的下面:

 _
Public ReadOnly Property InvalidMessages() As ArrayList
    Get
        Dim sMessage As String
        Dim sControlError As String
        Dim ctrl As Control
        Dim colInvalidMessages As New SortedList()

        ' Loop through controls and find which ones are invalid.
        ' Get the invalid message for each.
        ' Stuff the messages into a SortedList 
        ' using the TabOrder for the control to 
        ' establish the correct order.
        For Each ctrl In htProvidedProperties.Keys
            sMessage = GetErrorMessage(ctrl)
            If Not sMessage = String.Empty Then
                colInvalidMessages.Add(ctrl.TabIndex, sMessage)
            End If
        Next

        Dim colErrorsByIndex As New ArrayList()
        Dim obj As Object
        For Each obj In colInvalidMessages.Values
            sMessage = CType(obj, String)
            colErrorsByIndex.Add(sMessage)
        Next
        Return colErrorsByIndex
    End Get
End Property

 Public ReadOnly Property ValidationSummary() As String
    Get
        Dim sMessage As String
        Dim sControlError As String
        Dim ctrl As Control
        Dim colInvalidMessages As ArrayList

        ' Get invalid messages ordered by TabOrder of controls
        colInvalidMessages = Me.InvalidMessages

        ' Append together to get a complete summary. 
        ' Messages are separated by carriage return - line feeds.
        Dim ctlIndex As Object
        For Each ctlIndex In colInvalidMessages
            Dim sThisMessage As String
            sThisMessage = CType(ctlIndex, String)

            If Len(sThisMessage) > 0 Then
                If Len(sMessage) > 0 Then
                    sMessage &= vbCrLf
                End If
                sMessage &= " - " & sThisMessage
            End If
        Next
        Return sMessage

    End Get
End Property

借助于这两个属性,您可以将挂起的验证消息取到集合中(采用 ArrayList 的形式),或者,将其取到使用 CrLf 分隔的单个字符串中,以便在消息框中显示。

16.

到此为止,扩展程序提供程序便完成了。将其生成为 DLL,就可以在 Windows 窗体项目中加以使用了。

返回页首返回页首

在项目中使用扩展程序提供程序

使用扩展程序提供程序的第一步是启动一个 Windows 窗体项目,然后将扩展程序提供程序放在工具箱中。为此,请右键单击工具箱,然后选择 Customize Toolbox(如果使用的是 Visual Studio .NET 2002)或 Add/Remove Items(如果使用的是 Visual Studio .NET 2003)。浏览至名为 Validators.dll 的 DLL(它包含 MSDNTextboxValidator 组件),然后选择 DLL。当您返回到工具箱后,该组件将立即出现在工具箱中可用控件列表的底部。

要在 Windows 窗体项目中使用它,只须将其拖放到窗体上。您还需要拖放 ErrorProvider。然后,在 MSDNTextboxValidator 控件的 Properties 窗口中,将 ErrorProvider 属性设置为刚刚添加到窗体中的 ErrorProvider 控件。

此时,您可以将文本框添加到窗体中,并且设置其各种属性以便进行验证。您很可能希望试验一下不同的验证功能。图 4 显示了一个在无效文本框旁边有一些图标的典型窗体,图 5 显示了一个位于消息框中典型的验证摘要。

vbnet04082003-fig04

图 4. 显示了一些由 MSDNTextboxValidator 验证的文本框的窗体。

vbnet04082003-fig05

图 5. 由 MSDNTextboxValidator 生成的验证摘要。

需要注意的是,用于生成图 4图 5 的测试示例在窗体中恰好具有一行代码(当然,除了由设计器生成的代码以外)。该行位于 Validation Summary 按钮的 click 事件中,并且如下所示:

MsgBox(MsdnTextboxValidator1.ValidationSummary)

这表明,为了获取必填字段、范围等的验证功能,您不需要编写大量代码。您只须声明相应的属性,验证就会发生。

您或许确实需要在执行保存操作之前,确保没有挂起的验证错误信息。以上大约是您为使用 MSDNTextboxValidator 通常编写的所有代码。

返回页首返回页首

小结

MSDNTextboxValidator 的完整版本(包含在下载资料中)添加了一些附加功能,包括:

改变含有无效数据的文本框的背景色。

将错误信息放在其他控件中,如工具提示、标签和状态栏。

在具有数值数据类型的文本框中监控键击,并丢弃不适当的键击(如字母)。

我的产品版还有一种方法,可以将自定义验证逻辑集成到验证过程中。不过,我没有将该功能包括到下载资料中。如果读者有足够的兴趣,我将在以后撰写专栏文章以解释该功能的原理。

扩展程序提供程序对于许多设计任务都很有用。例如,您可以编写一个扩展程序提供程序,向所有控件添加安全分级,并使控件能够根据安全设置自动使整组控件不可见。无论何时,只要您意识到您正在对大量不同的控件重复执行相同种类的操作,扩展程序提供程序就会是您的代码重复问题的潜在解决方案。

Billy Hollis 与 Rocky Lhotka 共同编写了 VB.NET Programming with the Public Beta(这是第一本关于 Visual Basic .NET 的书),并且定期在主要行业会议上发表演讲。在 2001 年,他曾是 Microsoft 的 MSDN 区域主持人,现在他是 Microsoft 的 INETA 联络处的成员。他具有自己的 .NET 咨询业务,专门从事商业软件和智能客户端的开发。他还在全美国范围内提供 Visual Basic.NET 培训。

Posted on 2008-03-05 11:54  Chio  阅读(443)  评论(0编辑  收藏  举报
©2008 Suprasoft Component Intelligence Inc., All right reserved.