使用数据源时Datagridview拖放行

Posted

技术标签:

【中文标题】使用数据源时Datagridview拖放行【英文标题】:Datagridview drag&drop rows when using datasource 【发布时间】:2016-07-02 09:52:54 【问题描述】:

我正在尝试启动并运行我的第一个应用程序,但我在 datagridview 控件中的拖放操作中遇到了困难。

我创建了一个数据网格视图,其中连接了一个数据源。

Public oBodyAssembly As New BindingList(Of BodyComponent)
DataGridView1.DataSource = oBodyAssembly

在这个DataSource中,用户创建了新的对象,这些对象显示在datagridview中。为了让用户更正或更改他添加对象的初始顺序,我想让他们 拖放行 以重新排列对象在 grid 中的位置,并且在数据源中。

我尝试了这个用 C# 编写的示例代码并将其更改为 VB.NET,它的工作原理是我可以确定我拖动的行并确定拖放的位置。 Link to the example code

但是示例中的代码会插入新行并删除旧行。这对我不起作用。删除工作正常,并且该对象也从我的 DataSource 中删除。另一方面,新行的插入不会。

我的数据源是一个BindingList(Of BodyComponent),它只包含从BodyComponent类派生的对象。

我怎样才能让这个操作起作用?我被卡住了..

这是我目前用于拖放操作的代码。

    Public oRowIndexMouseDown As Integer
Public oRow As DataGridViewRow

Private Sub BodyAssemblyDrag_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles DataGridView1.MouseDown
    If DataGridView1.SelectedRows.Count = 1 Then
        If e.Button = MouseButtons.Left Then
            oRow = DataGridView1.SelectedRows(0)
            oRowIndexMouseDown = DataGridView1.SelectedRows(0).Index
            'Debug.Print("Row to move = " & oRowIndexMouseDown)

            DataGridView1.DoDragDrop(sender, DragDropEffects.Move)
        End If
    End If
End Sub

Private Sub BodyAssemblyDrag_dragenter(ByVal sender As Object, ByVal e As DragEventArgs) Handles DataGridView1.DragEnter
    If DataGridView1.SelectedRows.Count = 1 Then
        e.Effect = DragDropEffects.Move
    End If
End Sub

Private Sub BodyAssemblyDrag_dragdrop(ByVal sender As Object, ByVal e As DragEventArgs) Handles DataGridView1.DragDrop
    Dim oPoint As Point
    oPoint = DataGridView1.PointToClient(New Point(e.X, e.Y))

    Dim oRowIndexMouseDrop As Integer
    oRowIndexMouseDrop = DataGridView1.HitTest(oPoint.X, oPoint.Y).RowIndex

    'Debug.Print("Drop row @ " & oRowIndexMouseDrop)

    If Not oRowIndexMouseDrop = oRowIndexMouseDown Then
        'DataGridView1.Rows.RemoveAt(oRowIndexMouseDown)
        'DataGridView1.Rows.Insert(oRowIndexMouseDrop, oRow)
    End If
End Sub

Screenshot of winform

添加:在列表中创建对象的方法。

    Public oBodyAssembly As New List(Of BodyComponent)


Private Sub BTN_BODY_ADD_CILINDER_Click(sender As Object, e As EventArgs) Handles BTN_BODY_ADD_CILINDER.Click

    ' Create a new cylinder and add it into the oBodyAssembly
    Dim oCylinder As New Body_Cylinder
    oBodyAssembly.Add(oCylinder)

    ' Set the index number for this cylinder
    oCylinder.Index = oBodyAssembly.Count

    ' Set the component type
    oCylinder.Type = BodyComponent.BodyComponentType.Cylinder

End Sub

Private Sub BTN_BODY_ADD_CONE_Click(sender As Object, e As EventArgs) Handles BTN_BODY_ADD_CONE.Click

    ' Create a new cone and add it into the oBodyAssembly
    Dim oCone As New Body_Cone
    oBodyAssembly.Add(oCone)

    ' Set the index number for this cylinder
    oCone.Index = oBodyAssembly.Count

    ' Set the component type
    oCone.Type = BodyComponent.BodyComponentType.Cone_reduction

End Sub

类:

Public Class BodyComponent

' Basic properties that are required for all of the bodycompenents
' regardless of the type.
Public Property Index() As Double
Public Property Type() As BodyComponentType
Public Property Height() As Double
Public Property Thickness() As Double
Public Property Elevation() As Double
Private Property Mass() As Double

' Type Enum that defines what kind of body component is created.
Public Enum BodyComponentType
    Cylinder = 0001
    Cone_reduction = 0002
End Enum End Class

派生对象(圆锥相同)

Public Class Body_Cylinder

' Get the base properties
Inherits BodyComponent

' Set new properties that are only required for cylinders
Public Property Segments() As Integer
Public Property LW_Orientation() As Double End Class

【问题讨论】:

您将需要不同的收藏。您无法对 BindingList 进行排序或重新排序。您确定要在鼠标按下时启动 DargDrop 吗? 感谢您的快速回复,那您建议什么收藏?我对集合类型不是很熟悉。不应该在鼠标按下时启动拖放操作。它应该向下移动?但是我以前没有做过这样的事情,知道怎么写。 【参考方案1】:

首先,由于无法对 BindingList 进行排序或排序(无需重新创建整个集合),我将使用简单的 List(Of T)BindingSource

' Form level declarations:
Private Animals As List(Of AnimalEx)
Private BSAnimal As BindingSource

然后,一旦创建列表:

Animals = New List(Of AnimalEx)
' add Animals aka BodyComponent objects, then...
BSAnimal = New BindingSource(Animals, Nothing)
dgv.DataSource = BSAnimal

您将不得不学习一些管理数据的新方法。从现在开始,List 保存数据,但BindingSource 提供绑定功能,您可以对List 执行某些操作,而通过BindingSource 执行某些操作。


至于行拖放,this answer 中的代码是一个不错的起点,但缺少一些东西。它不考虑 a) 绑定的 DGV,b) 尝试拖动 NewRow 的用户,c) 用户单击 DGV 的非行区域(空/打开部分) d) 允许鼠标执行其他操作,例如调整列大小。我修复了这些,但可能还有其他鼠标操作可以豁免。

' Form-level declarations
Private fromIndex As Integer = -1
Private bMouseDn As Boolean = False
Private MouseDnPt As Point = Point.Empty

Private Sub dgv_DragOver(sender As Object, e As DragEventArgs) Handles dgv.DragOver
    e.Effect = DragDropEffects.Move
End Sub

Private Sub dgv_MouseDown(sender As Object, e As MouseEventArgs) Handles dgv.MouseDown
    bMouseDn = (e.Button = Windows.Forms.MouseButtons.Left)
End Sub

Private Sub dgv_MouseMove(sender As Object, e As MouseEventArgs) Handles dgv.MouseMove
    If bMouseDn Then
        ' first time, just grab the start location
        If (MouseDnPt = Point.Empty) Then
            MouseDnPt = e.Location
            Exit Sub
        End If
    End If
    If bMouseDn AndAlso MouseDnPt <> Point.Empty Then
        Dim hitTst = dgv.HitTest(e.X, e.Y)
        If hitTst IsNot Nothing AndAlso fromIndex = -1 AndAlso hitTst.RowIndex > -1 Then
            fromIndex = hitTst.RowIndex

            If dgv.Rows(fromIndex).IsNewRow = False Then
                dgv.DoDragDrop(dgv.Rows(fromIndex), DragDropEffects.Move)
            End If
        End If
    End If
End Sub

Private Sub dgv_MouseUp(sender As Object, e As MouseEventArgs) Handles dgvDD.MouseUp
    If bMouseDn AndAlso (e.Button = Windows.Forms.MouseButtons.Left) Then
        bMouseDn = False
    End If
End Sub

我使用了一个简单的Point 代替了Rectangle,它测试了非行区域点击,并且仅在鼠标移动并按下左键时才开始拖动。它也拒绝拖放 NewRow。

和原版一样,它拖着一个DataGridViewRow。但由于我们想要(必须)更改 DataSource,而不是 DGV 行,我们必须从 DataSource 取回项目:

Private Sub dgv_DragDrop(sender As Object, e As DragEventArgs) Handles dgv.DragDrop

    Dim p As Point = dgv.PointToClient(New Point(e.X, e.Y))
    Dim dragIndex = dgv.HitTest(p.X, p.Y).RowIndex
    If (e.Effect = DragDropEffects.Move) Then
        ' cast to a row
        Dim dragRow As DataGridViewRow = CType(e.Data.GetData(GetType(DataGridViewRow)), 
                                  DataGridViewRow)
        ' get related Animal object
        Dim a As AnimalEx = CType(dragRow.DataBoundItem, AnimalEx)

        ' manipulate DataSource:
        BSAnimal.RemoveAt(fromIndex)
        BSAnimal.Insert(dragIndex, a)

        ' if the DGV is SingleSelect, you may want:
        'dgv.Rows(dragIndex).Selected = True

        ' we are done dragging
        bMouseDn = False
        fromIndex = -1
        MouseDnPt = Point.Empty
    End If

End Sub

结果:

提到的“非行”区域是黄色区域。

【讨论】:

嗨,我目前正在尝试 List(of.. ) 但它对我不起作用,正如我所愿。当我创建数据绑定时,Public oBodyAssembly As List(Of BodyComponent) Private BSAnimal As BindingSource Public Sub New() ' This call is required by the designer. InitializeComponent() ' Add assembly to body grid. oBodyAssembly = New List(Of BodyComponent) BSAnimal = New BindingSource(oBodyAssembly, Nothing) DataGridView1.DataSource = BSAnimal End Sub 然后将一个新对象添加到 list(of ..) 中,它在 dgv 中不可见? 据我所知,从未格式化的代码中可以看出,您的列表中没有任何项目。请注意,答案设置 DGV 数据源 之后 列表有项目。 BindingSource“填补”了您在 BindingList 中的一些角色。使用 BSAnimal.Add(New AnimalEx With ...) 将项目添加到 List/BindingSource 好的,我添加了用于在列表中创建项目的代码,但我现在知道了,如果我通过 bindingsource 添加项目,它就可以工作。正如您在我编辑的帖子中看到的那样,我直接将它们添加到我的列表中。 它实际上最终在列表中,您只需要通过 BS 就可以知道更新 UI。 我认为您在编辑时不小心没有包含鼠标向上事件?

以上是关于使用数据源时Datagridview拖放行的主要内容,如果未能解决你的问题,请参考以下文章

在两个单独的 QTableWidgets 之间拖放行

QTableView拖放行无法正常工作

DataGridView更新数据到数据库

Vuetify v-data-table 拖放

如何将 DataGridView 行拖放到彼此下方?

在按钮单击时在有界 dataGridView 中添加新行