Visual C# 2005实现在两个列表之间来回拖放项目

2010-08-28 10:50:09来源:西部e网作者:

另外一项常见的拖放需求,就是在两个列表(ListView控件)之间来回拖放项目。事实上,我们经常会通过一组按钮来将列表中被选取的项目移至另外一个列表中,不过这样的操作模式需要两次的鼠标按键操作(第一次选取项目,第二次单击按钮)。显然在这样的操作需求中,拖放操作会较受青睐,因为它只需单一操作即可完成(选取并拖曳)。

\

图8.16在两个列表间来回拖放项目

图8.16所示是程序范例CH8_DemoForm014.vb的运行界面。显然,您可以在两个列表间来回拖放一个或多个文件来移动项目。本程序范例的设计重点说明如下:
  • 由于两个列表的ListView控件都可以作为置放目标,因此务必将这两个ListView控件的AllowDrop属性设定成True。
  • 请将两个ListView控件的MultiSelect属性设定成True。
  • 请将两个ListView控件的FullRowSelect属性设定成True。
  • 以下是程序范例CH8_DemoForm014.vb的程序代码内容。在Load事件处理函数中所调用的 PopulateListView()程序主要是用来初始化两个ListView控件:
    Private Sub CH4_DemoForm067_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs)Handles MyBase.Load
    Me.PopulateListView()
    End Sub

    Private Sub ListView_ItemDrag(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.ItemDragEventArgs)_
    Handles ListView1.ItemDrag, ListView2.ItemDrag

    Dim myItem As ListViewItem
    Dim myItems(CType(sender, ListView).SelectedItems.Count-1)_
    As ListViewItem
    Dim i As Integer=0

    ' 循环处理拖放源的SelectedItems集合。
    For Each myItem In CType(sender, ListView).SelectedItems
    ' 将ListViewItem添加到ListViewItems的数组中。
    myItems(i)=myItem
    i=i+1
    Next
    ' 创建一个DataObject对象来含有ListViewItem的数组。
    CType(sender, ListView).DoDragDrop(New _
    DataObject(_
    "System.Windows.Forms.ListViewItem()", myItems), _
    DragDropEffects.Move)
    End Sub

    Private Sub ListView_DragEnter(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.DragEventArgs)_
    Handles ListView1.DragEnter, ListView2.DragEnter
    ' 检查自定义的DataFormat ListViewItem数组。
    If e.Data.GetDataPresent(_
    "System.Windows.Forms.ListViewItem()")Then
    e.Effect=DragDropEffects.Move
    Else
    e.Effect=DragDropEffects.None
    End If
    End Sub

    Private Sub ListView_DragDrop(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.DragEventArgs)_
    Handles ListView1.DragDrop, ListView2.DragDrop
    Dim myItem As ListViewItem
    Dim myItems()As ListViewItem=_
    CType(_
    e.Data.GetData(_
    "System.Windows.Forms.ListViewItem()"), _
    ListViewItem())
    Dim i As Integer=0

    For Each myItem In myItems
    ' 将项目添加到目标列表中。
    Dim item As New ListViewItem(myItems(i).Text)
    item.SubItems.Add(myItems(i).SubItems(1).Text)
    CType(sender, ListView).Items.Add(item)

    ' 从源列表删除项目。
    If sender Is ListView1 Then
    ListView2.Items.Remove(ListView2.SelectedItems.Item(0))
    Else
    ListView1.Items.Remove(ListView1.SelectedItems.Item(0))
    End If
    i=i+1
    Next
    End Sub
    您或许会觉得奇怪,为什么不使用ListBox控件而要使用ListView控件。最主要的理由是,ListBox控件并不支持拖曳多个项目,单击列表会使得多重选择失效。
    ListView与TreeView控件都拥有一个ItemDrag事件来促进拖曳。在本范例中,我们使用单一个ItemDrag事件处理函数来处理两个ListView控件的ItemDrag事件。其中的sender参数代表初始化拖拽的控件。
    由于DataFormats类的成员并不包括ListViewItem类型,所以数据必须被当作一个系统Type来传递。ItemDrag事件处理函数的程序代码会创建一个类型为ListViewItem的数组并循环处理SelectedItems集合以便填入数组。在DoDragDrop方法中,一个新的DataObject对象会被创建并以数组来填入。您可以使用相同的技巧来拖放任何的系统Type。
    在DragDrop事件处理函数中,我们会将DataObject对象中的数组复制到一个新的ListViewItem数组中,而且每一个ListViewItem会被添加到目标ListView控件的Items集合中。
    在两个TreeView之间来回拖放节点

    \

    图8.17如何在TreeView之间来回拖放节点

    \

    图8.18如何在TreeView之间来回拖放节点  
    图8.17与8.18所示是程序范例CH8_DemoForm015.vb的运行界面。显然,您可以在两个TreeView控件间来回拖放一个节点(移动或复制)。本程序范例的程序代码如下所列:
    ' 声明一个常量以便侦测在拖曳期间CTRL键是否被按下。
    Const CtrlMask As Byte=8

    ' 处理两个TreeView控件的ItemDrag事件。
    Private Sub TreeView_ItemDrag(ByVal sender As System.Object, _
    ByVal e As System.Windows.Forms.ItemDragEventArgs)_
    Handles tvwRight.ItemDrag, tvwLeft.ItemDrag
    If e.Button=Windows.Forms.MouseButtons.Left Then
    ' 初始化拖放操作。
    DoDragDrop(e.Item, _
    DragDropEffects.Move Or DragDropEffects.Copy)
    End If
    End Sub

    ' 处理两个TreeView控件的DragDrop事件。
    Private Sub TreeView_DragDrop(ByVal sender As System.Object, _
    ByVal e As System.Windows.Forms.DragEventArgs)_
    Handles tvwLeft.DragDrop, tvwRight.DragDrop
    ' 此变量用来持有被用户所拖曳的节点。
    Dim OriginationNode As TreeNode=_
    CType(_
    e.Data.GetData(_
    "System.Windows.Forms.TreeNode"), TreeNode)

    ' 为一个TreeView控件调用GetDataPresent方法与替
    ' 一个文字或图像的方式一点不同,原因是TreeNode并不是
    ' DataFormats类的一个成员。也就是说,它不是一个预先
    ' 定义的类型。诸如此种状况,您必须使用能够接受一个字符串
    ' 作为类型的重载版本。
    If e.Data.GetDataPresent(_
    "System.Windows.Forms.TreeNode", False)Then
    Dim pt As Point
    Dim DestinationNode As TreeNode

    ' 取得鼠标指针所在位置的工作区坐标(Client Coordinate)。
    pt=CType(sender, TreeView).PointToClient(_
    New Point(e.X, e.Y))

    ' 选取鼠标指针所在位置之下的节点。
    DestinationNode=_
    CType(sender, TreeView).GetNodeAt(pt)

    ' 此处的If语句用来确保当用户在他们尝试
    ' 去拖曳之节点的上方不小心放开鼠标按键的话,
    ' 不会失去节点。如果您没有去检查目标节点是否
    ' 就是来源节点,将会使得该节点消失掉。
    If DestinationNode.TreeView _
    IsNot OriginationNode.TreeView Then
    DestinationNode.Nodes.Add(_
    CType(OriginationNode.Clone, TreeNode))
    ' 当添加一个新的节点时展开父节点,如此才会
    ' 清楚地呈现出拖放操作的结果。如果没有这样
    ' 做的话,将会显示一个+号。
    DestinationNode.Expand()
    ' 如果CTRL键没有被按下,就将原来的节点删除
    ' 以便实现移动节点的拖放操作。
    If(e.KeyState And CtrlMask)<> CtrlMask Then
    OriginationNode.Remove()
    End If
    End If
    End If
    End Sub

    ' 处理两个TreeView控件的DragEnter事件。
    Private Sub TreeView_DragEnter(ByVal sender As System.Object, _
    ByVal e As System.Windows.Forms.DragEventArgs)_
    Handles tvwRight.DragEnter, tvwLeft.DragEnter
    ' 检查被拖曳的数据的类型是否适用于目标控件。
    ' 如果不适用,则拒绝置放。
    If(e.Data.GetDataPresent(_
    "System.Windows.Forms.TreeNode"))Then
    ' 如果在拖曳期间按着CTRL键,则执行复制操作;
    ' 反之,则执行移动操作。
    If(e.KeyState And CtrlMask)=CtrlMask Then
    e.Effect=DragDropEffects.Copy
    Else
    e.Effect=DragDropEffects.Move
    End If
    Else
    e.Effect=DragDropEffects.None
    End If
    End Sub
    关键词:VS.NET