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
项。
就是说,这里还有一种方法可以避免动态创建列表(Animal
是 ClientLocation
的类型):
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
。
我正在尝试创建一个用于拖放 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
项。
就是说,这里还有一种方法可以避免动态创建列表(Animal
是 ClientLocation
的类型):
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
。