Share via


Changing row position in datatable

Question

Monday, October 15, 2012 4:40 PM | 1 vote

I have a datatable linked to a datagridview.

What I want to do is move selected rows in the datagridview to another specified position. And I want the move to be done in the datatable.

I am using this code:

     unlink_datasource1() 'code used to temporary unlink table and datafrigview For i = 0 To Form1.DataSet1.Tables(0).Rows.Count - 1 'for each row in table
            If Form1.DataGridView1.Rows.Item(i).Selected = True Then 'if selected
                Dim chosenraw, newrow As DataRow 'define rows
                chosenraw = Form1.DataSet1.Tables(0).Rows(i) '"old" row
                newrow = chosenraw '"copy" to a new row
                Form1.DataSet1.Tables(0).Rows.Remove(chosenraw) 'remove old
                Form1.DataSet1.Tables(0).Rows.InsertAt(newrow, position_to_place) 'insert new
            End If nextrelinkdatasource1 () 'code to relink table an datagridview

My problem is that the rows inserted are blank, instead of having the oldrow data.

Any suggestions?

All replies (11)

Tuesday, October 16, 2012 2:17 PM ✅Answered

Hello again,

I am going to provide you with another example that works off a BindingSource which has a DataTable as it's DataSource. Yes I understand you are using a DataTable in a DataSet so the only difference really is the BindingSource which allows us to get the current position in the DataGridView. So if you are willing to step out of your current mindset this will do the following. Lastly I did not attempt to reproduce what you have but instead give you how I would do this.

  • Press a button to move the current row up (test for top index)
  • Press a button to move the current row down (test for botton index)

Requires

  • VS2008 or higher (current code in VS2010, for VS2008 you need to add line continuations)
  • VB.NET
  • Option Strict On
  • Option Infer On
  • One DataGridView
  • Two Button controls

As you can see I include complete code showing the data comes from an MS-Access 2007 database table.

Public Class Form1    WithEvents bsData As New BindingSource    Private BuilderAccdb As New OleDb.OleDbConnectionStringBuilder With        {            .Provider = "Microsoft.ACE.OLEDB.12.0",            .DataSource = IO.Path.Combine(Application.StartupPath, "Database1.accdb")        }    Private Sub Form1_Load(        ByVal sender As System.Object,        ByVal e As System.EventArgs) Handles MyBase.Load        Dim dt = Load_Customers()        bsData.DataSource = dt        DataGridView1.AllowUserToAddRows = False        DataGridView1.DataSource = bsData        DataGridView1.CurrentCell = DataGridView1(1, 0)        For Each Column As DataGridViewColumn In DataGridView1.Columns            Column.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells        Next        DataGridView1.Columns("CompanyName").HeaderText = "Company"        DataGridView1.Columns("ContactName").HeaderText = "Contact"        DataGridView1.Columns("ContactTitle").HeaderText = "Title"    End Sub    Public Function Load_Customers() As DataTable        Using cn As New OleDb.OleDbConnection With            {                .ConnectionString = BuilderAccdb.ConnectionString            }            Using cmd As New OleDb.OleDbCommand With {.Connection = cn}                cmd.CommandText =                    <SQL>                        SELECT TOP 10                             Identifier,                             CompanyName,                             ContactName,                             ContactTitle                         FROM                             Customer                         Order By CompanyName                    </SQL>.Value                Dim dt As New DataTable                cn.Open()                dt.Load(cmd.ExecuteReader)                dt.Columns("Identifier").ColumnMapping = MappingType.Hidden                dt.AcceptChanges()                Return dt            End Using        End Using    End Function    Private Sub cmdMoveUp_Click(        ByVal sender As System.Object,        ByVal e As System.EventArgs) Handles cmdMoveUp.Click        Dim CurrentColumnIndex As Integer = DataGridView1.CurrentCell.ColumnIndex        Dim NewIndex As Int32 = CInt(IIf(bsData.Position = 0, 0, bsData.Position - 1))        Dim dt = CType(bsData.DataSource, DataTable)        Dim RowToMove As DataRow = DirectCast(bsData.Current, DataRowView).Row        Dim celldata(dt.Columns.Count - 1) As Object        celldata = RowToMove.ItemArray        Dim NewRow As DataRow = dt.NewRow        NewRow.ItemArray = celldata        dt.Rows.RemoveAt(bsData.Position)        dt.Rows.InsertAt(NewRow, NewIndex)        dt.AcceptChanges()        bsData.Position = NewIndex        DataGridView1.CurrentCell = DataGridView1(CurrentColumnIndex, NewIndex)    End Sub    Private Sub cmdMoveDown_Click(        ByVal sender As System.Object,        ByVal e As System.EventArgs) Handles cmdMoveDown.Click        Dim CurrentColumnIndex As Integer = DataGridView1.CurrentCell.ColumnIndex        Dim UpperLimit As Int32 = bsData.Count - 1        Dim NewIndex As Int32 = CInt(IIf(                bsData.Position + 1 >= UpperLimit, UpperLimit, bsData.Position + 1))        Dim dt = CType(bsData.DataSource, DataTable)        Dim RowToMove As DataRow = DirectCast(bsData.Current, DataRowView).Row        Dim celldata(dt.Columns.Count - 1) As Object        celldata = RowToMove.ItemArray        Dim NewRow As DataRow = dt.NewRow        NewRow.ItemArray = celldata        dt.Rows.RemoveAt(bsData.Position)        dt.Rows.InsertAt(NewRow, NewIndex)        dt.AcceptChanges()        bsData.Position = NewIndex        DataGridView1.CurrentCell = DataGridView1(CurrentColumnIndex, NewIndex)    End Sub    Private Sub cmdClose_Click_1(        ByVal sender As System.Object,        ByVal e As System.EventArgs) Handles cmdClose.Click        Close()    End SubEnd Class

Screenshot

KSG


Monday, October 15, 2012 5:03 PM

You do that with the currencymanager (that is not about money) we have more samples about that on our website.

http://www.vb-tips.com/CurrencyManager.aspx

Success
Cor


Monday, October 15, 2012 5:55 PM

The following works via Drag-n-Drop. Requires a DataGridView and 1 button control. Note you need to be aware of sorting, if the data source is sorted it will appear the move did not work when performing the move.

Form code

Public Class frmReorder    WithEvents bsPeople As New BindingSource    Private Identifier As String = ""    Private MouseDownRectangle As Rectangle    Private rowIndexFromMouseDown As Integer    Private rowIndexOfItemUnderMouseToDrop As Integer    <System.Diagnostics.DebuggerStepThrough()> _    Private Sub dataGridView1_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles DataGridView1.MouseMove        If (e.Button And MouseButtons.Left) = MouseButtons.Left Then            If MouseDownRectangle <> Rectangle.Empty AndAlso (Not MouseDownRectangle.Contains(e.X, e.Y)) Then                Dim dropEffect As DragDropEffects = DataGridView1.DoDragDrop(DataGridView1.Rows(rowIndexFromMouseDown), DragDropEffects.Move)            End If        End If    End Sub    <System.Diagnostics.DebuggerStepThrough()> _    Private Sub dataGridView1_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles DataGridView1.MouseDown        rowIndexFromMouseDown = DataGridView1.HitTest(e.X, e.Y).RowIndex        If rowIndexFromMouseDown <> -1 Then            Dim dragSize As Size = SystemInformation.DragSize            MouseDownRectangle = New Rectangle(New Point(e.X - (dragSize.Width \ 2), e.Y - (dragSize.Height \ 2)), dragSize)        Else            MouseDownRectangle = Rectangle.Empty        End If    End Sub    <System.Diagnostics.DebuggerStepThrough()> _    Private Sub dataGridView1_DragOver(ByVal sender As Object, ByVal e As DragEventArgs) Handles DataGridView1.DragOver        e.Effect = DragDropEffects.Move    End Sub    Private Sub dataGridView1_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs) Handles DataGridView1.DragDrop        Dim OriginalIdentifier As String = ""        Dim clientPoint As Point = DataGridView1.PointToClient(New Point(e.X, e.Y))        rowIndexOfItemUnderMouseToDrop = DataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex        If e.Effect = DragDropEffects.Move Then            OriginalIdentifier = bsPeople.CurrentRow(PrimaryIdentifier)            Dim rowToMove As DataGridViewRow = TryCast(e.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)            Dim celldata(rowToMove.Cells.Count - 1) As Object            For col As Integer = 0 To rowToMove.Cells.Count - 1                celldata(col) = rowToMove.Cells(col).Value            Next            Dim row As DataRow = bsPeople.DataTable.NewRow()            row.ItemArray = celldata            bsPeople.DataTable.Rows.InsertAt(row, rowIndexOfItemUnderMouseToDrop)            rowToMove.DataGridView.Rows.Remove(rowToMove)            If Not String.IsNullOrEmpty(bsPeople.Sort) Then                bsPeople.Sort = ""                cmdSort.Text = "Sort"            End If            If Not String.IsNullOrEmpty(OriginalIdentifier) Then                bsPeople.Locate(PrimaryIdentifier, OriginalIdentifier)            End If        End If    End Sub    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load        Dim ds As New DataSet        ds.ReadXml("People.xml")        bsPeople.DataSource = ds.Tables("Person")        DataGridView1.DataSource = bsPeople        bsPeople.Sort = "LastName"        DataGridView1.Columns(4).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill        For Each col As DataGridViewColumn In DataGridView1.Columns            col.SortMode = DataGridViewColumnSortMode.NotSortable        Next    End Sub    Private Sub bsPeople_PositionChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles bsPeople.PositionChanged        If bsPeople.Current IsNot Nothing Then            Identifier = bsPeople.PrimaryKey            Label1.Text = String.Format("BindingSource Primary key [{0}]", bsPeople.PrimaryKey)        End If    End Sub    Private Sub Sorter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSort.Click        If String.IsNullOrEmpty(bsPeople.Sort) Then            bsPeople.Sort = "LastName"            cmdSort.Text = "unsort"        Else            bsPeople.Sort = ""            cmdSort.Text = "Sort"        End If    End Sub    Private Sub DataGridView1_Sorted(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataGridView1.Sorted        bsPeople.Locate(PrimaryIdentifier, Identifier)    End SubEnd Class

Code module code

Module Extensions   Public Function PrimaryIdentifier() As String      Return KeyIdentifier   End Function   <System.Diagnostics.DebuggerStepThrough()> _   <System.Runtime.CompilerServices.Extension()> _   Public Function Locate(ByVal sender As BindingSource, ByVal Key As String, ByVal Value As String) As Integer      Dim Position As Integer = -1      Position = sender.Find(Key, Value)      If Position > -1 Then         sender.Position = Position      End If      Return Position   End Function   Private KeyIdentifier As String = "ID"   <System.Diagnostics.DebuggerStepThrough()> _   <System.Runtime.CompilerServices.Extension()> _   Public Function IsNewRow(ByVal sender As BindingSource) As Boolean      Return sender.CurrentRow.RowState = DataRowState.Detached   End Function   <System.Diagnostics.DebuggerStepThrough()> _   <System.Runtime.CompilerServices.Extension()> _   Public Function CurrentRow(ByVal sender As BindingSource) As DataRow      Return DirectCast(sender.Current, DataRowView).Row   End Function   <System.Diagnostics.DebuggerStepThrough()> _   <System.Runtime.CompilerServices.Extension()> _   Public Function PrimaryKey(ByVal sender As BindingSource) As String      Dim Value As String = "New row"      If Not sender.IsNewRow() Then         Value = DirectCast(sender.Current, DataRowView).Row(KeyIdentifier).ToString      End If      Return Value   End Function   <System.Diagnostics.DebuggerStepThrough()> _   <System.Runtime.CompilerServices.Extension()> _   Public Function CurrentRow(ByVal sender As BindingSource, ByVal Column As String) As String      Return DirectCast(sender.Current, DataRowView).Row(Column).ToString   End Function   <System.Diagnostics.DebuggerStepThrough()> _   <System.Runtime.CompilerServices.Extension()> _   Public Function DataTable(ByVal sender As BindingSource) As DataTable      Return DirectCast(sender.DataSource, DataTable)   End FunctionEnd Module

People.xml place in same folder as your executable

<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/css" href="people.css"?><People>    <Person>    <ID>246-12-5645</ID>    <FirstName>Kevin</FirstName>    <LastName>Gallagher</LastName>    <Age>40</Age>    <TimeStamp>20 March 2009</TimeStamp>  </Person>  <Person>    <ID>246-12-5646</ID>    <FirstName>Kevin</FirstName>    <LastName>MIlls</LastName>    <Age>41</Age>    <TimeStamp>12 September 2009</TimeStamp>  </Person>  <Person>    <ID>246-12-5647</ID>    <FirstName>Jon</FirstName>    <LastName>Wilson</LastName>    <Age>40</Age>    <TimeStamp>20 March 2009</TimeStamp>  </Person>  <Person>    <ID>246-12-5648</ID>    <FirstName>Brian</FirstName>    <LastName>Jones</LastName>    <Age>43</Age>    <TimeStamp>20 June 2004</TimeStamp>  </Person>  <Person>    <ID>246-12-5649</ID>    <FirstName>Sue</FirstName>    <LastName>Olson</LastName>    <Age>34</Age>    <TimeStamp>20 December 1999</TimeStamp>  </Person>  <Person>    <ID>246-12-5650</ID>    <FirstName>Ardythe</FirstName>    <LastName>Smith</LastName>    <Age>30</Age>    <TimeStamp>20 March 2009</TimeStamp>  </Person>  <Person>    <ID>246-12-5651</ID>    <FirstName>Greg</FirstName>    <LastName>White</LastName>    <Age>33</Age>    <TimeStamp>01 April 2001</TimeStamp>  </Person>  <Person>    <ID>246-12-5652</ID>    <FirstName>Cleve</FirstName>    <LastName>Owens</LastName>    <Age>55</Age>    <TimeStamp>20 March 2009</TimeStamp>  </Person></People>

KSG


Tuesday, October 16, 2012 7:17 AM

Thanks for your replies.

But why my code doesn't work?

If I put

MsgBox(newrow.Item(0))

after newrow=chosenrow the message that pops-up is correct. I mean the data displayed in it is really the data that is contained to oldrow.item(0). That means copying is done. So why when newrow is inserted cells are blank?


Tuesday, October 16, 2012 7:51 AM

And I want the move to be done in the datatable.

I did not noticed this part earlier. Be aware that a datatable represents a database. The position of a row in a database table is complete irrelevant (and can not be changed).

You need the options to show it to give it a certain order. 

Success
Cor


Tuesday, October 16, 2012 8:00 AM

I didn't know that a row's position cannot be changed in a database.

Even then, what my code is supposed to do is copy the row, paste the new copied row somewhere else and then delete the original row. I mean it doesn't actually change the row's position, it copies the row and then deletes it.

Couldn't that work?


Tuesday, October 16, 2012 8:40 AM

Now I see what your problem is, 

A Remove, removes from the datatable completely including information to do that in the database. 

A Delete marks a row to be removed and at an update it is removed from the database and from the datatable.

Form1.DataSet1.Tables(0).Row(i).Delete 'remove old

Be aware that you have to do this from end to begin so

For i = Form1.DataSet1.Tables(0).Rows.Count - 1 to 0 steps -1 'for each row in table

Don't ask me why they made it so difficult by putting the Remove method in the DataRowCollection and the Delete as a property of Row

Success
Cor


Tuesday, October 16, 2012 10:30 AM

Thanks for your replies.

But why my code doesn't work?

If I put

MsgBox(newrow.Item(0))

after newrow=chosenrow the message that pops-up is correct. I mean the data displayed in it is really the data that is contained to oldrow.item(0). That means copying is done. So why when newrow is inserted cells are blank?

Hard to say as for one there are two lines of code at the start and end that I have no clue to what they do. Also as I mentioned prior, if the data is sorted then that will make the move appear not to have work although it did work. So a simple sort will mask the move.

KSG


Tuesday, October 16, 2012 11:16 AM

@  Kevininstructor

The

unlink_datasource1() relink_datasource1()

subs are used justto make the code run faster as by using them multiple refreshes of datagridview are avoided.

Those subs can be omitted. Then the result is still the same blank rows.


Tuesday, October 16, 2012 11:26 AM

Did you handle my reply or are you ignoring it?

Did not make that code for fun.

Success
Cor


Tuesday, October 16, 2012 11:27 AM

Did you handle my reply or are you ignoring it?

Did not make that code for fun.

Success
Cor