List(of T) 的语法 ListBox DragDrop 类型 Naming/Conversion

Syntax for List(of T) Type Naming/Conversion for ListBox DragDrop

我正在尝试创建一个用于拖放 ListBox.Selecteditems 的通用例程,从源代码中获取 ListBox.SelectedItems 并创建一个列表(T)。为此,我需要能够获得底层 ListBox.SelectedValue 的类型,并相信我能够使用以下行实现这一点:

Dim lbxType As Type = (sender.SelectedValue).GetType

这是我的上下文子程序:

Private Sub ListBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
  If ListBox1.SelectedItems.Count = 0 OrElse e.Button <> Windows.Forms.MouseButtons.Right Then Return
  Dim lbxType As Type = (sender.SelectedValue).GetType
  Dim selItems As New List(Of lbxType)   <- FAILS! it fails here - will not compile
  {…more code…}
End Sub

除此之外,显然,我不知道如何创建上面代码中所示类型的 List(Of T) 的语法(请参阅标记为 'FAILS' 的行)。我知道我总能得到一个字符串 TypeName() 然后执行 Select/Case 但我希望有更多的 "modular" 方法。任何帮助或指导将不胜感激。

谢谢!

编辑#1 结果

Private Sub LbxTestDragStart(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown, ListBox3.MouseDown
    Dim lbx = DirectCast(sender, ListBox)
    If lbx.SelectedItems.Count = 0 OrElse e.Button <> Windows.Forms.MouseButtons.Right Then Return
    Dim dragLst = CType(GetType(List(Of )).
        MakeGenericType(lbx.SelectedItems(0).GetType()).
        GetConstructor(Type.EmptyTypes).
        Invoke(Nothing), IList)
    For Each si In lbx.SelectedItems
        dragLst.Add(si)
    Next
    lbx.DoDragDrop(dragLst, DragDropEffects.Copy)
End Sub

Private Sub Lbx_DragEnter(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragEnter, ListBox3.DragEnter
    Dim lbx = DirectCast(sender, ListBox)
    'we assume only one type of format in each listbox
    Dim fmt = e.Data.GetFormats(0)
    Dim fmtName As String = TypeName(e.Data.GetData(fmt(0))(0))
    e.Effect = DragDropEffects.None
    'id specific target listbox to set DD effects as this is a multi-purpose sub
    'in this select case, for testing, listbox3 is noticeably absent
    Select Case lbx.Name
        Case "ListBox1"
            'do something if ListBox1
        Case "ListBox2"
            If fmtName = "ClientLocation" Then e.Effect = DragDropEffects.Copy
    End Select
End Sub

Private Sub Lbx_DragDrop(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragDrop, ListBox3.DragDrop
    Dim lbx = DirectCast(sender, ListBox)
    Dim fmt = e.Data.GetFormats(0)
    Dim fmtName As String = TypeName(e.Data.GetData(fmt(0))(0))
    'setup for processing of dropped items
    Select Case lbx.Name
        Case "ListBox2"
            If fmtName = "ClientLocation" Then
                For Each item In e.Data.GetData(fmt(0))
                    lbx.Items.Add(item)
                Next
            End If
    End Select
End Sub

正如我在评论中所说,拖放之间的时间太少了,真的不值得创建一个通用列表。放置目标应检查放置的类型,以便仅接受 ClientLocation 项而不接受 CircusClown 项。

就是说,这里还有一种方法可以避免动态创建列表(AnimalClientLocation 的类型):

Private dragList As List(Of Animal)

Private Sub lb_MouseMove(sender ...
    If lbMouseDown Then

        dragList = New List(Of Animal)
        For Each a As Animal In lb.Items
            dragList.Add(a)
        Next

        lb.DoDragDrop(dragList, DragDropEffects.Copy)
        ...

您的代码假装不知道 ListBox 中的类型。如果那可能是因为它可能是几种类型中的一种 - 也许内容改变了(?),那么你必须求助于 Reflection:

' get the Type for a List(of T)
Dim genType = GetType(List(Of ))
' get the type in the listbox
Dim t As Type = lb.SelectedItems(0).GetType() 'GetType(Animal) 
' get the "combined" type for List(of ) + ListType
Dim LType = genType.MakeGenericType(t)     ' == List(of Animal)

此时,你可以有两种方式:

' METHOD 1: using a ctor
Dim ctor As ConstructorInfo = LType.GetConstructor(Type.EmptyTypes)
Dim theList = CType(ctor.Invoke(Nothing), IList)

' METHOD 2: using System.Activator:
Dim theList = CType(Activator.CreateInstance(LType), IList)

' EITHER WAY, xfer items - see note
For Each aa In lb.SelectedItems
    theList.Add(aa)
Next
lb.DoDragDrop(theList, DragDropEffects.Copy)

为说明目的而设置格式。您可以使用 GetConstructor 版本链接方法并避免所有临时类型变量:

Dim dragLst = CType(GetType(List(Of )).
    MakeGenericType(lb.SelectedItems(0).GetType()).
    GetConstructor(Type.EmptyTypes).
    Invoke(Nothing), IList)

注意:通常,您会希望在 DragDrop 启动时添加鼠标悬停的项目 - 用户会有所期待。该项目可能不是 SelectedItems 的一部分,他们可能只想拖动该项目。这意味着找到 e.X、e.Y

下的项目

为简单起见,代码仅使用 SelectedItems