将表单作为 属性 添加到具有设计器支持的用户控件

Adding a Form as a property to a user control with Designer support

我创建了一个自定义标签。它有一个 Form 类型的 属性,用户可以 select 设计器中的表单,然后在单击标签时加载该表单。它工作正常,但属性下拉列表中的唯一形式是标签所在的形式。

有没有办法显示所有可能的表单,或者让用户将表单作为字符串传递,然后将其转换为表单。

这是我的标签的代码:


Public Class BMLabel
    Private t As String
    Private id As String
    Private frm As New Form

    <Category("BM")>
    Public Property Type As String
        Get
            Return t
        End Get
        Set(value As String)
            t = value
        End Set
    End Property

    Public Property Identifier As String
        Get
            Return id
        End Get
        Set(value As String)
            id = value
        End Set
    End Property

    Public Property Form As Form
        Get
            Return frm
        End Get
        Set(value As Form)
            frm = value
        End Set
    End Property

    Private Sub BMLabel_Click(sender As Object, e As EventArgs) Handles Me.Click
        Dim t = frm.GetType()

        Dim form As Form = DirectCast(Activator.CreateInstance(t), Form)
        form.ShowDialog()
    End Sub
End Class

问题是,当您声明 Form 类型的 属性 时,它允许您 select 一个 表单实例 ,而不是一个继承自 Formtype/class。您需要做的是声明类型为 Type 的 属性 并准备允许您 select 所需类型的编辑器(将选项限制为继承自 Form).

Disclaimer: The idea of creating a custom editor was inspired by this answer and the code was adapted for this particular situation.

所以,我们开始吧。自定义标签 class 看起来像这样:

Public Class BMLabel
    Inherits Label
    ' Don't forget to change the namespace.
    '        ↓↓↓↓↓↓↓↓↓↓↓
    <Editor("WindowsApp1.TypeSelector, System.Design", GetType(UITypeEditor)), Localizable(True)>
    Public Property FormType As Type

    Private Sub BMLabel_Click(sender As Object, e As EventArgs) Handles Me.Click
        Using frm As Form = DirectCast(Activator.CreateInstance(FormType), Form)
            frm.ShowDialog(Me)
        End Using
    End Sub
End Class

现在,我们需要创建 TypeSelector class:

Public Class TypeSelector
    Inherits UITypeEditor

    Public Overrides Function GetEditStyle(context As ITypeDescriptorContext) As UITypeEditorEditStyle
        If context Is Nothing OrElse context.Instance Is Nothing Then
            Return MyBase.GetEditStyle(context)
        End If

        Return UITypeEditorEditStyle.Modal
    End Function

    Public Overrides Function EditValue(context As ITypeDescriptorContext, provider As IServiceProvider, value As Object) As Object
        Dim editorService As IWindowsFormsEditorService

        If context Is Nothing OrElse context.Instance Is Nothing OrElse provider Is Nothing Then
            Return value
        End If

        editorService = DirectCast(provider.GetService(GetType(IWindowsFormsEditorService)),
                                   IWindowsFormsEditorService)

        Dim dlg As New FormTypeSelector()
        dlg.Value = DirectCast(value, Type)
        dlg.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
        If editorService.ShowDialog(dlg) = System.Windows.Forms.DialogResult.OK Then
            Return dlg.Value
        End If
        Return value
    End Function
End Class

然后,我们创建一个名为 FormTypeSelector 的表单,其中包含 ComboBoxListBox 以列出可用选项:

Public Class FormTypeSelector
    Friend Property Value As Type

    Private Sub FormTypeSelector_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim availableFormTypes =
            System.Reflection.Assembly.GetExecutingAssembly().
                    GetTypes().
                    Where(Function(t) t.BaseType = GetType(Form) AndAlso t <> Me.GetType()).ToList()
        cboFormTypes.DisplayMember = "Name"
        cboFormTypes.DataSource = availableFormTypes
        cboFormTypes.SelectedItem = Value
    End Sub

    Private Sub BtnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
        Value = DirectCast(cboFormTypes.SelectedItem, Type)
        DialogResult = DialogResult.OK
        Close()
    End Sub

    Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
        DialogResult = DialogResult.Cancel
        Close()
    End Sub
End Class

就是这样;它应该准备好了:

注意:您可能需要在 FormTypeSelector 中添加一个选项以允许清除 FormType 属性 的 selected 值,这应该很容易去做。

有两种服务可以帮助您在设计时发现和解析解决方案中的所有类型:

另一方面,要在 属性 编辑器的下拉列表中显示标准值,您可以创建 TypeConverter:

  • TypeConverter提供将值类型转换为其他类型以及访问标准值和子属性的统一方法。

了解以上选项后,您可以创建一个自定义类型转换器来发现项目中的所有表单类型并在下拉列表中列出。

例子

在下面的示例中,我创建了一个自定义按钮 class,它允许您在设计类型中 select 表单类型,然后在 运行 时,如果您单击按钮,它将 selected 形式显示为对话框:

To see a C# version for this answer see .

我的按钮

Imports System.ComponentModel
Public Class MyButton
    Inherits Button
    <TypeConverter(GetType(FormTypeConverter))>
    Public Property Form As Type
    Protected Overrides Sub OnClick(ByVal e As EventArgs)
        MyBase.OnClick(e)
        If Form IsNot Nothing AndAlso GetType(Form).IsAssignableFrom(Form) Then

            Using f = CType(Activator.CreateInstance(Form), Form)
                f.ShowDialog()
            End Using
        End If
    End Sub
End Class

FormTypeConverter

Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Globalization

Public Class FormTypeConverter
    Inherits TypeConverter
    Public Overrides Function GetStandardValuesExclusive(ByVal context As ITypeDescriptorContext) As Boolean
        Return True
    End Function
    Public Overrides Function CanConvertTo(ByVal pContext As ITypeDescriptorContext, ByVal pDestinationType As Type) As Boolean
        Return MyBase.CanConvertTo(pContext, pDestinationType)
    End Function
    Public Overrides Function ConvertTo(ByVal pContext As ITypeDescriptorContext, ByVal pCulture As CultureInfo, ByVal pValue As Object, ByVal pDestinationType As Type) As Object
        Return MyBase.ConvertTo(pContext, pCulture, pValue, pDestinationType)
    End Function
    Public Overrides Function CanConvertFrom(ByVal pContext As ITypeDescriptorContext, ByVal pSourceType As Type) As Boolean
        If pSourceType = GetType(String) Then Return True
        Return MyBase.CanConvertFrom(pContext, pSourceType)
    End Function
    Public Overrides Function ConvertFrom(ByVal pContext As ITypeDescriptorContext, ByVal pCulture As CultureInfo, ByVal pValue As Object) As Object
        If TypeOf pValue Is String Then Return GetTypeFromName(pContext, CStr(pValue))
        Return MyBase.ConvertFrom(pContext, pCulture, pValue)
    End Function
    Public Overrides Function GetStandardValuesSupported(ByVal pContext As ITypeDescriptorContext) As Boolean
        Return True
    End Function
    Public Overrides Function GetStandardValues(ByVal pContext As ITypeDescriptorContext) As StandardValuesCollection
        Dim types As List(Of Type) = GetProjectTypes(pContext)
        Dim values As List(Of String) = New List(Of String)()
        For Each type As Type In types
            values.Add(type.FullName)
        Next
        values.Sort()
        Return New StandardValuesCollection(values)
    End Function
    Private Function GetProjectTypes(ByVal serviceProvider As IServiceProvider) As List(Of Type)
        Dim typeDiscoverySvc = CType(serviceProvider.GetService(GetType(ITypeDiscoveryService)), ITypeDiscoveryService)
        Dim types = typeDiscoverySvc.GetTypes(GetType(Object), True).Cast(Of Type)().Where(Function(item) item.IsPublic AndAlso GetType(Form).IsAssignableFrom(item) AndAlso Not item.FullName.StartsWith("System")).ToList()
        Return types
    End Function
    Private Function GetTypeFromName(ByVal serviceProvider As IServiceProvider, ByVal typeName As String) As Type
        Dim typeResolutionSvc As ITypeResolutionService = CType(serviceProvider.GetService(GetType(ITypeResolutionService)), ITypeResolutionService)
        Return typeResolutionSvc.[GetType](typeName)
    End Function
End Class