1

I've a custom structure:

Public Structure myStruct
   Public Sub New(ByVal row As Integer, ByVal Col As Integer)
      X = row : Y = Col
   End Sub
   Property X() As Integer
   Property Y() As Integer
end Structure

That I'm using as a Property in a UserControl:

Public Class myUC
   inherits UserControl

   Property myProp1 As String = "test"
   Property MyProp2 As myStruct = New myStruct(1, 2)
End Class

If I put this UserControl on a Form the Windows Form Designer will create a piece of code in the InitializeComponent method that looks like this:

'
'MyUC1
'
Me.MyUC1.Location = New System.Drawing.Point(205, 187)
Me.MyUC1.myProp1 = "test"

Me.MyUC1.MyProp2 = CType(resources.GetObject("MyUC1.MyProp2"), WindowsApp11.myStruct)
Me.MyUC1.Name = "MyUC1"
Me.MyUC1.Size = New System.Drawing.Size(150, 150)
Me.MyUC1.TabIndex = 0

But I would like to have myStruct serialized like the System.Drawing.Point structure. Thus the resulting Designer code should be

'
'MyUC1
'
Me.MyUC1.Location = New System.Drawing.Point(205, 187)
Me.MyUC1.myProp1 = "test"
Me.MyUC1.MyProp2 = new WindowsApp11.myStruct(1,2)
Me.MyUC1.Name = "MyUC1"
Me.MyUC1.Size = New System.Drawing.Size(150, 150)
Me.MyUC1.TabIndex = 0

Has anyone an idea how to tell the Windows Form Designer how to serialize the custom structure. I was thinking of something like the Serialization.IXmlSerializable interface, but for Design-Time.

I've tried to find something using Google, but I seem to use the wrong search expressions.

Thx

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
marcus
  • 13
  • 2
  • You need to implement a type converter and override a few methods. You will find an example in [this answer](https://stackoverflow.com/a/55060816/3110834). – Reza Aghaei Jan 07 '21 at 18:55
  • And here is [source code of `PointConverter`](https://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/PointConverter.cs,0e3a4309646c3111). The key methods for this feature are `GetCreateInstanceSupported` and `CreateInstance`. – Reza Aghaei Jan 07 '21 at 19:00
  • I reopen as it's a VB question. – Reza Aghaei Jan 08 '21 at 07:55
  • Thank you for this. This was exactly the solution I was looking for! It works like a charm! – marcus Jan 08 '21 at 11:43
  • No problem, I posted a VB answer to help future readers. – Reza Aghaei Jan 08 '21 at 11:43

1 Answers1

0

You need to implement a custom TypeConverter which can convert your object to InstanceDescriptor, then the designer generate the serialized code using the constructor of your struct.

To do so you need to derive from TypeConverter or one of classes which derive from TypeConverter, like ExpandableObjectConverter, then override a few methods.

Example

  1. Create a VB.NET Windows Forms Application. (The application project)

  2. Create a VB.NET Control Library Application. (The control project)

  3. Add MyPoint.vb and paste the following code inside it to add MyPoint structure and MyPointConverter class:

     Imports System.ComponentModel
     Imports System.ComponentModel.Design.Serialization
     Imports System.Globalization
    
     <TypeConverter(GetType(MyPointConverter))>
     Public Structure MyPoint
         Public Property X As Integer
         Public Property Y As Integer
         Public Sub New(X As Integer, Y As Integer)
             Me.X = X
             Me.Y = Y
         End Sub
     End Structure
    
     Public Class MyPointConverter
         Inherits ExpandableObjectConverter
         Public Overrides Function CanConvertTo(context As ITypeDescriptorContext, destinationType As Type) As Boolean
             If destinationType = GetType(InstanceDescriptor) Then
                 Return True
             End If
             Return MyBase.CanConvertTo(context, destinationType)
         End Function
         Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As CultureInfo, value As Object, destinationType As Type) As Object
             If destinationType = GetType(InstanceDescriptor) Then
                 Dim ci = GetType(MyPoint).GetConstructor(New Type() {GetType(Integer), GetType(Integer)})
                 MessageBox.Show(value.ToString())
                 Dim i = DirectCast(value, MyPoint)
                 Return New InstanceDescriptor(ci, New Object() {i.X, i.Y})
             End If
             Return MyBase.ConvertTo(context, culture, value, destinationType)
         End Function
         Public Overrides Function GetCreateInstanceSupported(context As ITypeDescriptorContext) As Boolean
             Return True
         End Function
         Public Overrides Function CreateInstance(context As ITypeDescriptorContext, propertyValues As IDictionary) As Object
             If (propertyValues Is Nothing) Then
                 Throw New ArgumentNullException(NameOf(propertyValues))
             End If
             Dim X = DirectCast(propertyValues(NameOf(MyPoint.X)), Integer)
             Dim Y = DirectCast(propertyValues(NameOf(MyPoint.Y)), Integer)
             Return New MyPoint(X, Y)
         End Function
     End Class
    
  4. Open UserControl1.vb and paste the following code to add MyPoint property:

     Public Class UserControl1
         Public Property MyPoint As MyPoint
     End Class
    
  5. Close all designers and rebuild the solution.

  6. Open the first project (application project) and open Form1 in design mode and from toolbox, drop an instance of UserControl1, change MyPoint property and save changes.

  7. see the InitializeComponent method and you see:

     'UserControl11
     '
     Me.UserControl11.Location = New System.Drawing.Point(65, 65)
     Me.UserControl11.MyPoint = New WindowsControlLibrary1.MyPoint(1, 2)
     Me.UserControl11.Name = "UserControl11"
     Me.UserControl11.Size = New System.Drawing.Size(150, 150)
     Me.UserControl11.TabIndex = 2
    

Tip

  1. Put the control in a different project than the application project to prevent some unexpected designer crash or exceptions in serialziation.

  2. To clean windows forms designer project assemblies, you need to delete subforlders of ProjectAssemblies folder which is located in %userprofile%\appdata\local\Microsoft\VisualStudio\ under one of the folders depending to your visual studio version and edition. For me, they are here:

     C:\Users\rag\AppData\Local\Microsoft\VisualStudio\16.0_dc21d183\ProjectAssemblies
    
  3. If you need to debug design time, follow the steps explained here: How to debug WinForms designer.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398