基于组合框选择动态添加自定义用户控件到表单,无需条件循环

Dynamically add custom usercontrol to form based on combobox selection without conditional looping

我正在从事一个项目,该项目要求用户为其客户创建数量不详的捕获字段。这样做时,我想创建一个通用模板,他们在其中键入新字段的名称,然后 select 组合框中的类型。组合框的值('free text'、'boolean'、'decimal' 等)可以存储在任何地方(即我对它们的绑定方式不挑剔)。 selectedIndexChanged 事件将处理适当用户控件的添加。但是,由于我不知道我将处理多少个值类型(现在看起来是 7 - 9),我想避免使用以下类型的代码:

Private Sub cbxType_SelectedIndexChanged(sender As Object, e As EventArgs)
    If cbxType.SelectedItem = "Free Text" Then
        Dim vText As VTText = New VTText() ' This is the usercontrol for text
        Controls.Add(vText)
    ElseIf cbxType.SelectedItem = "Boolean" Then
        Dim vBool As VTBool = New VTBool() ' etc.
    ...

我的第一个想法是在加载表单时使用某种 list/dictionary 键值对

Dim kList As List(Of KeyValuePair(Of String, Object)) = New List(Of KeyValuePair(Of String, Object))
kList.Add(New KeyValuePair(Of String, Object)("Text", New vText())
For each kpair As KeyValuePair(Of String, Object) In kList
    cbx.Items.Add(kPair.Key)
Next

在更改事件处理程序中使用类似的东西

Dim myControl = DirectCast(cbxType.SelectedItem, KeyValuePair(Of String, Object)).Value
Controls.Add(myControl)

但是,selectedvalue 属性为空,我得到一个无效的转换异常。我尝试转换为 Control 和 UserControl,但这些当然不起作用。 除了我不正确的实施,这种方法是否合理?有一个更好的方法吗?理想情况下,我想要一条可以有效执行以下操作的行:

Controls.Add(cbxType.SelectedValue)

您可以创建一个 class 来保存您的控件和显示文本,并将这些 objects 绑定为组合框的数据源。然后你就可以实现你的一个班轮来为你添加控件collection。

示例(假设我有一个带有组合框 (Combobox1) 和按钮 (Button1) 的表单:

Public Class Form1

   Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
       If (Not ComboBox1.SelectedItem Is Nothing) Then
          GroupBox1.Controls.Add(Activator.CreateInstance(ComboBox1.SelectedItem.ControlType))
       End If
   End Sub

   Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
       'add some controls to the cbx
       Dim ctrl As New MyControl
       ctrl.DisplayText = "TextBox"
       ctrl.ControlType = GetType(System.Windows.Forms.TextBox)
       Me.ComboBox1.Items.Add(ctrl)
      End Sub
End Class
Public Class MyControl 'The thing that i have also done is add the Display Memeber and Value Member on my Combobox.
    Public DisplayText As String
    Public ControlType As Type
End Class

这是另一个类似于 DotNetHitMan 之前建议的选项...

首先,覆盖 UserControls 中的 ToString() 以设置它们在 ComboBox 中的显示方式。例如:

Public Class VTText

    ' ... other code in your UserControl ...

    Public Overrides Function ToString() As String
        Return "Free Text"
    End Function

    ' ... other code in your UserControl ...

End Class

Public Class VTBool

    ' ... other code in your UserControl ...

    Public Overrides Function ToString() As String
        Return "Boolean"
    End Function

    ' ... other code in your UserControl ...

End Class

接下来,将 ComboBox 的 FormattingEnabled 设置为 False,然后将 UserControls 的实例直接添加到表单的 Load() 事件中的 ComboBox:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    ComboBox1.FormattingEnabled = False
    ComboBox1.Items.Add(New VTText)
    ComboBox1.Items.Add(New VTBool)
    ' ... etc ... (one entry per type of your UserControls)
End Sub

现在添加一个模块,以便您可以为控件创建一个扩展方法,允许您根据传入的控件类型创建一个新实例:

Public Module Extensions

    <Runtime.CompilerServices.Extension()>
    Public Function GetNewInstance(ByVal ctl As Control) As Control
        Return Activator.CreateInstance(ctl.GetType)
    End Function

End Module

最后,在 Button 单击处理程序中,将 ComboBox.SelectedItem 转换为 Control 并调用 GetNewInstance() 扩展函数:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    If ComboBox1.SelectedIndex <> -1 Then
        Dim ctl As Control = DirectCast(ComboBox1.SelectedItem, Control).GetNewInstance
        ' ... do other stuff with "ctl" ? ...
        FlowLayoutPanel1.Controls.Add(ctl)        
    End If
End Sub