在 .Designer 中缩写序列化

Abbreviate serialization in .Designer

我有一个 属性 类型为 Foo 的自定义控件。我为此 class 创建了一个 UITypeEditor

这很有效,设计器代码如下:

Dim Foo1 As PropertySerialization.Foo = New PropertySerialization.Foo()
Me.FooControl1 = New PropertySerialization.FooControl()
Me.SuspendLayout()
'
'FooControl1
'
Me.FooControl1.Location = New System.Drawing.Point(35, 56)
Me.FooControl1.Name = "FooControl1"
Me.FooControl1.Size = New System.Drawing.Size(188, 136)
Foo1.A = 3
Foo1.B = "World"
Me.FooControl1.Something = Foo1
Me.FooControl1.TabIndex = 0
Me.FooControl1.Text = "FooControl1"

Something 属性 是 Foo 类型,如您所见,设计者明确地创建了一个新对象 Foo1

我的问题是:我能否告诉设计者宁愿使用 With 关键字内联创建 foo 对象,例如:

'
'FooControl1
'
Me.FooControl1.Location = New System.Drawing.Point(35, 56)
Me.FooControl1.Name = "FooControl1"
Me.FooControl1.Size = New System.Drawing.Size(188, 136)
Me.FooControl1.Something = New Foo() With {.A = 3, .B = "World"}
Me.FooControl1.TabIndex = 0
Me.FooControl1.Text = "FooControl1"

我希望这样做是为了在创建更复杂的类型时避免设计器文件混乱。

非常感谢使用 C# 或 VB.NET 的答案。

Designer (VS) 将按照自己的意愿编写它,这在很大程度上取决于您的 Foo 类型是如何构建的以及它继承自什么。但总的来说,您可以提供一个 TypeConverter 并且至少可以使用构造函数参数获得更短的形式。

这是通过在 CanConvertToConvertTo 中回复 InstanceDescriptor 来完成的:

<Serializable>
<TypeConverter(GetType(FooConverter))>
Public Class Foo
    Inherits ??????

   <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
   Public Property Name As String

   <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
   Public Property Value As Integer

   Public Sub New(newName As String, v As Integer)
        Name = newName
        Value = v
   End Sub

    ' simple constructor
    Public Sub New()
        Name = "New Foo"
        Value = 0
    End Sub
End Sub

HiddenDesignerSerializationVisibility 属性值告诉 VS 不要去序列化那个 属性。目的是通过重载的构造函数来处理它。类型转换器:

Friend Class FooConverter
    Inherits TypeConverter

    ' designer serialization will check to see if 
    ' there is a converter available
    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
            Dim f As Foo = CType(value, Foo)

            ' prepare a constructor info
            Dim ctor As Reflection.ConstructorInfo

            ' the ctor wanted, is the one which takes a string, and an Integer
            ctor = GetType(Foo).GetConstructor(New Type() _
                          {GetType(String), GetType(Integer)})

            ' return Instance Descriptor built from ctor info and 
            '   an array of the current values for the ctor
            Return New InstanceDescriptor(ctor,
                        New Object() {f.Name, f.Value}, False)

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

End Class

上面的拼写是为了清楚起见,但您可以使用 shorthand:

Return New InstanceDescriptor(GetType(Foo). _
                              GetConstructor(New Type() _
       {GetType(String),
       GetType(Integer)}),
       New Object() {f.Name, f.Value}, False)

设计器代码结果仍将是一个明确的临时对象,但更紧凑一些,并且任何 其他 属性都将一一设置:

Dim Foo1 As Prj.Foo = New Prj.Foo("Ziggy", 43)

请注意,尾随 Boolean 参数指示 VS 是否应查找更多属性。 Boolean 和 属性 上的 DesignerSerializationVisibility 值的组合将决定是 VS 序列化值还是您的 TC 处理它。任何值都希望保留,应该由您的 TypeConverter 处理或设置为 .Visible.

最后,由于没有关于自定义控件或真实 Foo 类型的信息,所以很难知道如何应用一些细节(什么是可序列化的等)。

摘自 Enhanced CollectionEditor Framework,涵盖了简单的 TC 和设计器序列化。

就我个人而言,如果 VS 通常会做的任何事情都会起作用,那么似乎需要做很多额外的工作。