1

我有一个用户控件,它公开了自定义集合的属性。

这是用户控件中使用的代码。

Imports System.ComponentModel.DesignerSerializationVisibility

Public Class textbox
    Inherits System.Windows.Forms.TextBox
    Private _validation As New validationList

    <System.ComponentModel.DesignerSerializationVisibility(Content)>
    Public Property Validation As validationList
        Get
            Return _validation
        End Get
        Set(ByVal value As validationList)
            _validation = value
        End Set
    End Property
End Class

这是此属性使用的集合类。

Imports System.Collections.ObjectModel
<Serializable()> Public Class validationList
    Inherits Collection(Of validationItem)

    Public Shadows Sub Add(ByVal item As validationItem)
        '//Check for duplicates
        Dim dupe As Boolean = False
        For n As Int32 = 0 To Items.Count - 1
            If Items(n).Key = item.Key Then
                dupe = True
                Exit For
            End If
        Next
        If dupe = False Then
            Items.Add(item)
        End If
    End Sub
End Class

这是集合类使用的项目列表

<Serializable()> Public Class validationItem
    Private _key As validationTypes
    Private _value As String

    Public Sub New()
        '//Empty constructor is needed for serialization
    End Sub

    Public Sub New(ByVal k As validationTypes, ByVal v As String)
        _key = k
        _value = v
    End Sub

    Public Enum validationTypes
        Madatory = 1
        [Integer] = 2
        Numeric = 3
        [Decimal] = 4
        MaxValue = 5
        MinValue = 6
        MinLength = 7
        Email = 8
    End Enum

    Public Property Value As String
        Get
            Return _value
        End Get
        Set(ByVal Value As String)
            _value = Value
        End Set
    End Property

    Public Property Key As validationTypes
        Get
            Return _key
        End Get
        Set(ByVal value As validationTypes)
            _key = value
        End Set
    End Property
End Class

这是实现 Pluntonix 建议的解决方案后设计器代码的样子。

Dim ValidationItem1 As Testing_Project.validationItem = New Testing_Project.validationItem(Testing_Project.validationItem.validationTypes.MaxValue, "4")
Dim ValidationItem2 As Testing_Project.validationItem = New Testing_Project.validationItem(Testing_Project.validationItem.validationTypes.MinLength, "5")

Me.Textbox1.Validations.Add(ValidationItem1)
Me.Textbox1.Validations.Add(ValidationItem2)

我从设计器中添加了一些项目,并尝试在运行时检索它们,但所有键都设置为 0,值设置为 Nothing。我还需要通过设计器添加的项目的确切列表可用,我怎样才能使它工作,以便通过设计器添加的实际值也存在于运行时。

4

1 回答 1

3

对 UC 属性的一些更改:

'Imports System.ComponentModel.DesignerSerializationVisibility
Imports System.ComponentModel

' Changed name to be able to tell these from regular ones
Public Class SuperText   
    Inherits System.Windows.Forms.TextBox
    Private _validation As New validationList

    ' I changed name to PLURAL because it is a collection
    <System.ComponentModel.DesignerSerializationVisibility(Content)>
    Public Property Validations As validationList
        Get
            ' just to be sure:
            If _validation Is Nothing Then
               _validation = New validationList
            End If

            Return _validation
        End Get
        Set(ByVal value As validationList)
            ' you do NOT want anyone to be able to change your collection!
            '_validation = value
        End Set
    End Property

    ' missing some serialization elements:
    ' use the right ShouldSerializeXXXX / ResetXXX names
    ' where XXX == your property name
    ' this also controls whether the property shows as BOLD when there are items
    Private Function ShouldSerializeValidations As Boolean
        Return _validation.Count > 0
    End Function

    Private Sub ResetValidations
         ' often you do nothing here
         _validation = New validationList
    End Sub

End Class

项目类缺少所有序列化支持,这些是实际序列化的项目:

' these should show as text in a drop down, lets use 
' better names (just a suggestion)
' Moved out of Item class to make references shorter
Public Enum validationTypes
    IsRequired
    IsInteger

   ' maybe IsValue  how is Numeric this different from
   ' Integer or Decimal
    Numeric        
    IsDecimal 
    MaxValue 
    MinValue 
    MinLength 
    Email 
End Enum

 <TypeConverter(GetType(ValidationItemConverter))>   ' might need
 <Serializable()> 
 Public Class validationItem

    Public Sub New()
        '//Empty constructor is needed for serialization
        ' actually SIMPLE ctor is needed for the Collection Editor
        ' we dont want Nothings flying about:
        Key = validationTypes.IsRequired
        Value = "False"   ' whatever it should be
    End Sub

    ' we will probably need this for a TypeConverter
    Public Sub New(ByVal k As validationTypes, ByVal v As String)
        Key = k
        Value = v
    End Sub

    ' TODO:
    ' Add a NAME property (avoid MyThing + SuperText in Editor ListBox)
    ' or Override TOSTRING to return Key.ToString to 
    ' the method(s) to apply the rules is missing too obviously

    ' recent VS versions allow Auto Implemented props so no need for 
    ' a backing field...makes for less SO code too...

    ' we need to tell VS how to serialize this:
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)>
    Public Property Value As String

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)>
    Public Property Key As validationTypes

End Class

试试这个......它可能不会工作,但它可能会。使用DesignerSerializationVisibility.Visible>可能会导致 Item 值被序列化。如果没有,(即使有)你将需要一个TypeConverter.

所以你知道,这就是我们正在谈论的。在表单的 Sub New 中,深入到 InitializeComponent。添加到集合中的验证项的代码如下所示:

Dim FooBar4 As Plutonix.SomeClass.FooBar = 
                New Plutonix.MyThing.FooBar("NewFoo", "sdfsdf")
...

Me.MyThing.FooList.Add(FooBar4)
Me.MyThing.FooList.Add(FooBar5)
Me.MyThing.FooList.Add(FooBar6)

VS 需要帮助创建该代码,因为它不知道如何创建FooBarValidationItem添加到集合中。

这也是检查您距离确定序列化要求有多近的好方法。


您可能需要一个用于 VS 的 TypeConverter 来调用来为设计器代码创建对象。我们需要返回一个InstanceDescriptor. 这是与您的收藏类似的东西的代码,您应该能够适应:

Imports System.ComponentModel.Design.Serialization

Friend Class RowFilterConverter
    Inherits TypeConverter

    Public Overrides Function CanConvertTo(context As ITypeDescriptorContext,
                                           destType As Type) As Boolean
        If destType = GetType(InstanceDescriptor) Then
            ' Yes I Can
            Return True
        End If
        Return MyBase.CanConvertTo(context, destType)
    End Function

    Public Overrides Function ConvertTo(context As ITypeDescriptorContext,
                                        info As CultureInfo, value As Object,
                                        destType As Type) As Object

        If destType = GetType(InstanceDescriptor) Then
            ' convert value (the current instance) to Type
            Dim rf As RowFilter = CType(value, RowFilter)

            ' prepare a constructor info
            Dim ctor As Reflection.ConstructorInfo

            ' the ctor I want takes a string, Integer, enum, String
            ' validation item would be just the validationTypes enum, String
            ctor = GetType(RowFilter).GetConstructor(New Type() _
                                     {GetType(String),
                                      GetType(Integer),
                                      GetType(ExcludeOperators),
                                      GetType(String)})

            ' return Instance Descriptor built from ctor info 
            ' and an array of the current
            '   values for the ctor params
            Return New InstanceDescriptor(ctor,
                        New Object() {rf.Name, rf.FieldIndex, 
                                      rf.Operation, rf.Target}, True)

        End If
        Return MyBase.ConvertTo(context, info, value, destType)

    End Function

End Class
  • 添加<TypeConverter(GetType(ValidationItemConverter))>到您的 ValidationItem 类
  • 将 DesignerVisibility 值更改为,.Hidden因为我们是通过构造函数进行的
  • 强烈建议您在控件开始成熟后立即将其移至控件库 (DLL) 中,以免对其进行意外更改。NameSpace如果您最终在 Lib 中得到其他不密切相关的东西,请使用包装器。

有了 aTypeConverter和其他东西,你应该很高兴。您将需要经常构建和清理。VS 运行此代码,因此您要确保它没有使用过时的代码,否则您最终会追逐鬼魂。


这或多或少的工作方式是,在通过集合编辑器添加一些项目后,VS 将表单标记为脏,重写设计器文件(InitializeComponent 中的代码)然后重新加载表单(这就是它可能闪烁的原因)。

这反过来又会调用您的类Add方法来过滤掉骗子。我认为编辑器在打开时使用集合的临时副本,因此如果您可以取消,它只会返回原始版本。因此,在编辑器中,Add当您单击添加按钮时,您的代码不会运行。这就是为什么骗子没有在编辑器中过滤掉的原因。

当您关闭编辑器并使用新的设计器代码重新构建表单时,您Add确实会运行,但这意味着您将仅保留第一个使用IsFoo的实例,而其他实例将被丢弃。解决这个问题的方法是自定义集合编辑器来轮询集合类以查看是否可以向IsFoo集合添加新类型。

您必须决定自定义集合编辑器是否值得,或者仅保存验证规则的第一个实例就足够了。

于 2014-06-23T15:40:45.877 回答