【问题标题】:Move up or down little complex DataGridView rows with multi-selection使用多选向上或向下移动复杂的 DataGridView 行
【发布时间】:2015-06-14 18:20:06
【问题描述】:

我有一个这样的 DataGridView:

现在,在 C#VB.Net 中,我想用一个按钮将所选行向上或向下移动一个位置,我该怎么做?.多选要求使我复杂化了。

DataGridView中出现的文件会被合并成一个可执行文件,然后文件会按照“Order”列的顺序执行,所以“Order " 值在向上或向下移动行时应该是不可变的。

我没有使用任何数据源。


我试图从 MSDN 分析这个示例,它包含一些扩展方法,但它需要一个数据表和数据源,我对使用数据表和数据源没有任何不满,但是只是我不知道如何为我的 DataGridView 调整代码示例。 无论如何,示例不支持多选:

Move rows up/down and remember order for DataGridView and ListBoxs data bound

我还在 StackOverflow 上看到了一些关于此的 C# 问题,但他们要求选择单行:

How to move gridview selected row up/down on KeyUp or Keydown press

DataGridView Selected Row Move UP and DOWN

然后,我没有起点,只有这些方法可以移动一个也不保留 Order 值的单行,如果有人可以指导我根据我的需要扩展功能: Private Sub Button_MoveUp_Click(sender As Object, e As EventArgs) _ 处理 Button_MoveUp.Click

    Me.MoveUpSelectedRows(Me.DataGridView_Files)

End Sub

Private Sub Button_MoveDown_Click(sender As Object, e As EventArgs) _
Handles Button_MoveDown.Click

    Me.MoveDownSelectedRows(Me.DataGridView_Files)

End Sub

Private Sub MoveUpSelectedRows(ByVal dgv As DataGridView)

    Dim curRowIndex As Integer = dgv.CurrentCell.RowIndex
    Dim newRowIndex As Integer = curRowIndex - 1

    Dim curColIndex As Integer = dgv.CurrentCell.ColumnIndex
    Dim curRow As DataGridViewRow = dgv.CurrentRow

    If (dgv.SelectedCells.Count > 0) AndAlso (newRowIndex >= 0) Then

        With dgv
            .Rows.Remove(curRow)
            .Rows.Insert(newRowIndex, curRow)
            .CurrentCell = dgv(curColIndex, newRowIndex)
        End With

    End If

End Sub

Private Sub MoveDownSelectedRows(ByVal dgv As DataGridView)

    Dim curRowIndex As Integer = dgv.CurrentCell.RowIndex
    Dim newRowIndex As Integer = curRowIndex + 1

    Dim curColIndex As Integer = dgv.CurrentCell.ColumnIndex
    Dim curRow As DataGridViewRow = dgv.CurrentRow

    If (dgv.SelectedCells.Count > 0) AndAlso (dgv.Rows.Count > newRowIndex) Then

        With dgv
            .Rows.Remove(curRow)
            .Rows.Insert(newRowIndex, curRow)
            .CurrentCell = dgv(curColIndex, newRowIndex)
        End With

    End If

End Sub

更新

我正在尝试@Plutonix 方法(稍作修改),唯一的问题是它不能正确地将所选行向上移动。

重现问题的步骤:

  1. 选择两个在一起的行(例如,行索引 2 和行索引 3,NOT 行索引 2 和行索引 4)

  2. 尝试将行向上移动。

我该如何解决?

Public Enum MoveDirection As Integer
    Up = -1
    Down = 1
End Enum

Private Sub MoveRows(ByVal dgv As DataGridView, ByVal moveDirection As MoveDirection)

    Dim rows As DataGridViewRowCollection = dgv.Rows

    ' row index
    Dim thisRow As DataGridViewRow

    ' put selection back
    Dim selectedRows As New List(Of Integer)

    ' max rows
    Dim lastRowIndex As Integer =
        If(dgv.AllowUserToAddRows,
           rows.Count - 2,
           rows.Count - 1)



    For n As Integer = lastRowIndex To 0 Step -1

        If Not rows(n).IsNewRow Then

            If rows(n).Selected Then

                selectedRows.Add(n)

                MsgBox(n)

                Select Case moveDirection

                    Case Main.MoveDirection.Down
                        If ((n + moveDirection) <= lastRowIndex) AndAlso (n + moveDirection >= 0) AndAlso rows(n + moveDirection).Selected = False Then

                            selectedRows(selectedRows.Count - 1) = (n + moveDirection)
                            thisRow = rows(n)
                            rows.Remove(thisRow)

                            rows.Insert(n + moveDirection, thisRow)

                        End If

                    Case Main.MoveDirection.Up

                        If ((n + moveDirection) <= lastRowIndex) Then

                            MsgBox(selectedRows(selectedRows.Count - 1))
                            selectedRows(selectedRows.Count - 1) = (n + moveDirection)
                            thisRow = rows(n)
                            rows.Remove(thisRow)

                            rows.Insert(n + moveDirection, thisRow)

                        End If

                End Select

            End If

        End If

    Next n

    ' reselect the original selected rows
    For n As Integer = 0 To lastRowIndex

        dgv.Rows(n).Selected = selectedRows.Contains(n)

        ' renumber the order (optional & unknown, but trivial)
        dgv.Rows(n).Cells(0).Value = (n + 1)

    Next n

End Sub

【问题讨论】:

  • “订单”值应该是不可变的是什么意思?当这 2 行向下移动 1 行时,它们是 (2, 4) 还是 (3, 5)?不可变意味着它们不会改变,这意味着可见订单与订单显示的数字不匹配。这似乎令人困惑。
  • 是的,可见索引不应该改变,你完全理解我的意思,别担心:),谢谢。

标签: c# .net vb.net winforms datagridview


【解决方案1】:

(已更新)
您需要迭代行集合,测试是否每个都被选中,然后交换行。为了防止 2 个选定的行在到达顶部或底部时相互交换,需要单独的 Up 和 Down 方法。

' list of hash codes of the selected rows
Private Function GetSelectedRows() As List(Of Integer)
    Dim selR As New List(Of Integer)

    ' have to clear selected so the NEXT row 
    ' doesnt cause odd behavior
    For n As Integer = 0 To dgv.Rows.Count - 1
        If dgv.Rows(n).IsNewRow = False AndAlso dgv.Rows(n).Selected Then
            selR.Add(dgv.Rows(n).GetHashCode)
            dgv.Rows(n).Selected = False
        End If
    Next
    Return selR
End Function

' restore original selected rows
Private Sub SetSelectedRows(selRows As List(Of Integer))

    For n As Integer = 0 To dgv.Rows.Count - 1
        If dgv.Rows(n).IsNewRow = False Then
            dgv.Rows(n).Selected = selRows.Contains(dgv.Rows(n).GetHashCode)
            ' reset Order col:
            dgv.Rows(n).Cells(0).Value = n + 1
        End If
    Next
End Sub

Private Sub MoveRowsUp()
    ' short ref
    Dim rows As DataGridViewRowCollection = dgv.Rows
    ' row index
    Dim thisRow As DataGridViewRow
    ' put selection back
    Dim selectedRows As List(Of Integer)
    ' max rows
    Dim LastRow = If(dgv.AllowUserToAddRows, rows.Count - 2, rows.Count - 1)

    selectedRows = GetSelectedRows()

    For n As Int32 = 0 To LastRow
        If rows(n).IsNewRow = False Then

            If (selectedRows.Contains(rows(n).GetHashCode)) AndAlso (n - 1 >= 0) AndAlso
                (selectedRows.Contains(rows(n - 1).GetHashCode) = False) Then

                thisRow = rows(n)
                rows.Remove(thisRow)
                rows.Insert(n - 1, thisRow)
            End If
        End If
    Next

    SetSelectedRows(selectedRows)

End Sub

Private Sub MoveRowsDn()
    Dim rows As DataGridViewRowCollection = dgv.Rows
    Dim thisRow As DataGridViewRow
    Dim selectedRows As New List(Of Integer)
    Dim LastRow = If(dgv.AllowUserToAddRows, rows.Count - 2, rows.Count - 1)

    selectedRows = GetSelectedRows()

    For n As Int32 = LastRow To 0 Step -1

        If rows(n).IsNewRow = False Then
            If (selectedRows.Contains(rows(n).GetHashCode)) AndAlso (n + 1 <= LastRow) AndAlso
                         (selectedRows.Contains(rows(n + 1).GetHashCode) = False) Then
                thisRow = rows(n)
                rows.Remove(thisRow)
                rows.Insert(n + 1, thisRow)
            End If
        End If
    Next

    SetSelectedRows(selectedRows)

End Sub

用法:

 MoveRowsUp()
 ' move down:
 MoveRowsDn()

移动行会导致 dgv 重置选择。这些方法首先遍历并获取所选行的 HashCode 列表,然后在最后重置每个 Row.Selected 属性。

代码更改了 Cell(0) 的值,因此 order 和 Order 列匹配(这意味着 Order 列的值可变的)。

Moving 检查这行是否在末尾是否也选择了目的地的行。这可以防止行在到达顶部或底部时交换位置。

之前:

之后:

请注意,当“Zalgo”到达底部时(意味着选择了 3 和 5),另一个仍然可以向下移动一个,所以当“Ziggy”保持不动时它会这样做。 "Ziggy(3)" 是否下移的能力取决于下一行/索引(仅) - 下一行不超出底部 AND 未选中,因此它仍然可以下移1,而“Zalgo (5)”不能。

【讨论】:

  • 谢谢,这正是我需要的,但它不适用于向上方向,请看我的更新,我添加了一些修改,但原始代码有同样的问题。
  • Note that when "Zalgo" got to the bottom, the other one could still move down one, so it did while "Zalgo" stayed put and selected. 是的,我已经编写了一个临时修复程序,您可以在上面的更新中看到,因为我认为这是一个行为问题,最好的办法是,例如,如果您选择了第 2,4 和 5 行,然后将行向下移动,只有第 2 行向下移动到 3,所以 4 和 5 保持在相同的 lasts 索引中,我的意思是第 4 和 5 行已经在底部,它们应该仍然不能要向下移动,在这种情况下,只有第 2 行应该向下移动。我想出了如何在没有成功的情况下做到这一点。
  • 主要问题是关于 UP 方向,正如我在 udate 中提到的,关于顶行或底行叠加的行为问题是一个小问题,但请如果你能想出如何改进它那么这将是一个很棒的(也许效率更高的)datagridview 行移动机制!。
  • Up 对我来说很好用。 2、4、5 也可以,但有点奇怪。当测试 4 时,它可以向下移动,所以它确实如此。如果您不想在测试中添加AndAlso rows(n + direction).Selected = False,以便如果下一个位置被选中,则会跳过它。将其添加到已经存在的 2 AndAlso 的末尾,以便在 (n + direction > LastRow) 时跳过它。这也可能解决 2+3 问题 - 我将在早上并排查看。
  • 等等,你是说 (3, 5) 作为一个单元移动,而 3 不能向下移动,因为 5 在底部 - 即它们之间留有一个东西?
【解决方案2】:

这是我的最终代码,感谢@Plutonix,我刚刚将逻辑转换为扩展方法,还扩展了原始功能,通过提供一组单元格索引来自动保存单元格以保存其值:

#Region " Members Summary "

' · Public Methods
'
'     MoveSelectedRows(direction)
'     MoveSelectedRows(direction, preserveCellsIndex)

#End Region

#Region " Option Statements "

Option Strict On
Option Explicit On
Option Infer Off

#End Region

#Region " Imports "

Imports System.Diagnostics
Imports System.Runtime.CompilerServices
Imports System.Windows.Forms

#End Region

''' <summary>
''' Contains sofisticated extension methods for a <see cref="DataGridView"/> control.
''' </summary>
''' <remarks></remarks>
Public Module DataGridViewExtensions

#Region " Enumerations "

    ''' <summary>
    ''' Specifies a direction for a move operation of a rows collection.
    ''' </summary>
    Public Enum RowMoveDirection As Integer

        ''' <summary>
        ''' Move row up.
        ''' </summary>
        Up = 0

        ''' <summary>
        ''' Move row down.
        ''' </summary>
        Down = 1

    End Enum

#End Region

#Region " Public Extension Methods "

    ''' <summary>
    ''' Moves up or down the selected row(s) of the current <see cref="DataGridView"/>.
    ''' </summary>
    ''' <param name="sender">The <see cref="DataGridView"/>.</param>
    ''' <param name="direction">The row-move direction.</param>
    <DebuggerStepThrough>
    <Extension>
    Public Sub MoveSelectedRows(ByVal sender As DataGridView,
                                ByVal direction As RowMoveDirection)

        DoRowsMove(sender, direction)

    End Sub

    ''' <summary>
    ''' Moves up or down the selected row(s) of the current <see cref="DataGridView"/>.
    ''' </summary>
    ''' <param name="sender">The <see cref="DataGridView"/>.</param>
    ''' <param name="direction">The row-move direction.</param>
    ''' <param name="preserveCellsIndex">A sequence of cell indexes to preserve its cell values when moving the row(s).</param>
    <DebuggerStepThrough>
    <Extension>
    Public Sub MoveSelectedRows(ByVal sender As DataGridView,
                                ByVal direction As RowMoveDirection,
                                ByVal preserveCellsIndex As IEnumerable(Of Integer))

        DoRowsMove(sender, direction, preserveCellsIndex)

    End Sub

#End Region

#Region " Private Methods "

    ''' <summary>
    ''' Moves up or down the selected row(s) of the specified <see cref="DataGridView"/>.
    ''' </summary>
    ''' <param name="dgv">The <see cref="DataGridView"/>.</param>
    ''' <param name="direction">The row-move direction.</param>
    ''' <param name="preserveCellsIndex">Optionally, a sequence of cell indexes to preserve its cell values when moving the row(s).</param>
    <DebuggerStepThrough>
    Private Sub DoRowsMove(ByVal dgv As DataGridView,
                           ByVal direction As RowMoveDirection,
                           Optional ByVal preserveCellsIndex As IEnumerable(Of Integer) = Nothing)

        ' Keeps tracks of a cell value to preserve, to swap them when moving rows.
        Dim oldCellValue As Object
        Dim newCellValue As Object

        ' Short row collection reference.
        Dim rows As DataGridViewRowCollection = dgv.Rows

        ' Keeps track of the current row.
        Dim curRow As DataGridViewRow

        ' The maximum row index.
        Dim lastRowIndex As Integer =
            If(dgv.AllowUserToAddRows,
               rows.Count - 2,
               rows.Count - 1)

        ' List of hash codes of the selected rows.
        Dim selectedRows As New List(Of Integer)

        ' Get the hash codes of the selected rows
        For i As Integer = 0 To (rows.Count - 1)
            If (rows(i).IsNewRow = False) AndAlso (rows(i).Selected) Then
                selectedRows.Add(rows(i).GetHashCode)
                rows(i).Selected = False
            End If
        Next i

        ' Move the selected rows up or down.
        Select Case direction

            Case RowMoveDirection.Up
                For i As Integer = 0 To lastRowIndex

                    If Not rows(i).IsNewRow Then

                        If (selectedRows.Contains(rows(i).GetHashCode)) AndAlso
                           (i - 1 >= 0) AndAlso
                           (Not selectedRows.Contains(rows(i - 1).GetHashCode)) Then

                            curRow = rows(i)
                            rows.Remove(curRow)
                            rows.Insert(i - 1, curRow)

                            If preserveCellsIndex IsNot Nothing Then

                                For Each cellIndex As Integer In preserveCellsIndex
                                    oldCellValue = curRow.Cells(cellIndex).Value
                                    newCellValue = rows(i).Cells(cellIndex).Value

                                    rows(i).Cells(cellIndex).Value = oldCellValue
                                    curRow.Cells(cellIndex).Value = newCellValue
                                Next cellIndex

                            End If

                        End If

                    End If

                Next i

            Case RowMoveDirection.Down
                For i As Integer = lastRowIndex To 0 Step -1

                    If Not rows(i).IsNewRow Then

                        If (selectedRows.Contains(rows(i).GetHashCode)) AndAlso
                           (i + 1 <= lastRowIndex) AndAlso
                           (Not selectedRows.Contains(rows(i + 1).GetHashCode)) Then

                            curRow = rows(i)
                            rows.Remove(curRow)
                            rows.Insert(i + 1, curRow)

                            If preserveCellsIndex IsNot Nothing Then

                                For Each cellIndex As Integer In preserveCellsIndex
                                    oldCellValue = curRow.Cells(cellIndex).Value
                                    newCellValue = rows(i).Cells(cellIndex).Value

                                    rows(i).Cells(cellIndex).Value = oldCellValue
                                    curRow.Cells(cellIndex).Value = newCellValue
                                Next cellIndex

                            End If

                        End If

                    End If

                Next i

        End Select

        ' Restore selected rows.
        For i As Integer = 0 To (rows.Count - 1)

            If Not rows(i).IsNewRow Then
                rows(i).Selected = selectedRows.Contains(rows(i).GetHashCode)
            End If

        Next i

    End Sub

#End Region

End Module

【讨论】:

    猜你喜欢
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-20
    相关资源
    最近更新 更多