vb.netuser-controlscollectioneditor

VB.Net items added via the designer not reflected on code


I have a usercontrol which exposes a property of a custom collection.

Here is the code used in the usercontrol.

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

Here is the collection class that this property uses.

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

Here is the list of items that the collection class use

<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

Here is what the designer code looks like after implementing the solution suggested by 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)

I added a number of items to collection from the designer & I tried to retrieve them at runtime but all the keys are set to 0 and values are set to Nothing. I need the exact list of items added via the designer to be available as well, how can I make it work so that the actual values added via the designer exists at runtime as well.


Solution

  • A few changes to the UC property:

    '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
    

    The item class is missing all the serialization support, these are the items that actually get serialized:

    ' 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
    

    TRY this...it probably wont work, but it might. Using DesignerSerializationVisibility.Visible> might result in the Item values getting serialized. If not, (and even if it does) you will need a TypeConverter.

    So you know, this is what we are talking about. In your Sub New for the form, drill into the InitializeComponent. The code for your validation items getting added to the collection will look like this:

    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 needs help creating that code because it has no idea how to create a FooBar or ValidationItem to add to the collection.

    This is also a good way to examine how close you are to nailing the serialization requirements.


    You probably will need a TypeConverter for VS to invoke to create your objects for the designer code. We need to return an InstanceDescriptor. This is code for one from something which does something similar to your collection which you should be able to adapt:

    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
    

    With a TypeConverter and the other stuff, you should be good to go. You will need to Build and Clean often. VS runs this code so you want to be sure that it is not using stale code or you end up chasing ghosts.


    The way this more or less works is that after adding some items via the Collection Editor, VS marks the form as dirty, rewrites the designer file (the code in InitializeComponent) then reloads the form (thats why it might flicker).

    This in turn calls your class Add method which filters out the dupes. I think the Editor uses a temp copy of the collection while open so if you can CANCEL, it just returns the original version. So in the Editor, your Add code doesn't run when you click the ADD button. This is why dupes arent filtered out in the editor.

    You Add does run when you close the editor and the form is rebuilt with the new designer code, but that means you will be retaining only the first instance using IsFoo and the others discarded. The way around this is a custom collection editor to poll the collection class to see if it is okay to add a new IsFoo type to the collection.

    You will have to decide if a Custom Collection Editor is worth it or just saving the first instance of a validation rule is good enough.