问题
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.
回答1:
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
- Add
<TypeConverter(GetType(ValidationItemConverter))>
to your ValidationItem class - Change the DesignerVisibility value to
.Hidden
because we are doing them via the constructor - Strongly suggest you move the control to a Control Lib (DLL) as soon as it starts to reach maturity so that accidental changes to it are more difficult. use a
NameSpace
wrapper if you end up with other things in the Lib which are not closely related.
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.
来源:https://stackoverflow.com/questions/24369553/vb-net-items-added-via-the-designer-not-reflected-on-code