I have a dataGridView1
, dataGridView2
and a ButtonAdd
in one Form.
The user will:
1- select any one "Cell" or the "entire Row".
2-select multiple Rows
Then:
the data selected will be moved from dataGridView1 to dataGridView2 when the button clicked. that's all what i need to do.
My try:
I have tried this solution after many searches and it's almost done but there's a little problem I couldn't handle:
Full Code:
private const string strconneciton = @"YourConnectionString";
SqlConnection con = new SqlConnection(strconneciton);
SqlCommand cmd = new SqlCommand();
DataTable dataTable;
private void loadDataIntoGridView1()
{
try
{
con.Open();
cmd.CommandText = "SELECT id, accNum, accName FROM Employees";
cmd.Connection = con;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = cmd;
dataTable = new DataTable();
adapter.Fill(dataTable);
BindingSource bSource = new BindingSource();
bSource.DataSource = dataTable;
dataGridView1.DataSource = bSource;
//i don't know if this line is useful...
dataGridView2.DataSource = dataTable.Clone();
adapter.Update(dataTable);
con.Close();
}
catch (Exception ed)
{
con.Close();
MessageBox.Show(ed.Message);
}
}//end loadDataIntoGridView1
private void buttonSend_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedCells.Count > 0)
{
foreach (DataGridViewCell oneCell in dataGridView1.SelectedCells)
{
if (oneCell.Selected)
{
//this should add the rows that is selected from dataGridView1 and,
//pass it to dataGridView2
var currentRow = ((DataRowView)dataGridView1.CurrentRow.DataBoundItem).Row;
((DataTable)dataGridView2.DataSource).ImportRow(currentRow);
//this will remove the rows you have selected from dataGridView1
dataGridView1.Rows.RemoveAt(oneCell.RowIndex);
}//end if
}//end foreach
}//end if
}//end button click
Lets Debug:
just Note a one thing before starting:
- the method for deleting rows (multi or single) is working fine in all cases.
- the method for adding to the DGV2 is the problem, I had taken it from here ...it works fine when selecting a single row but not multiple.
1- if you have selected a one cell/row it will be added and removed successfully.
2- if you have selected a multiple rows lets say the first and the second it will add the second and the third, then definitely will deleted them, but only one is added..
why?!
Because here
var currentRow = ((DataRowView)dataGridView1.CurrentRow.DataBoundItem).Row;
((DataTable)dataGridView2.DataSource).ImportRow(currentRow);
gets the current index of the existing rows in the DGV1 and iterate to the number of the selecting rows and add them to DGV2.
Screenshot:
image of how the process is done:
.
what i need to do:
What should be done to solve this problem?
Although marco has a point, ownership of the row is to the table as exception thrown. To bypass this, you might want to do this way...
// creates a new row based on the table you are about to
// insert INTO with same structure
var copyRow = table.NewRow();
copyRow.ItemArray = currentRow.ItemArray;
table.Rows.Add( copyRow );
The ItemArray is a list of ALL the column properties of the table in order without explicit reference to Row["someColumn"]. So copying the array to the new row gets you all the columns. This is - provided both table structures are the same - and appears to be from your scenario provided.
Clarification
The above code only does a single row and would be applied based on your loop where the
if (oneCell.Selected)
You have multiple columns selected per row thus causing some confusion to your loops. You are actually hitting the same row multiple times. In any event, i found you are actually doing this in a Windows Application, not WPF.. I have created a form and posting the code below.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
loadDataIntoGridView1();
}
public DataTable tblFrom { get; set; }
public DataTable tblTo { get; set; }
private void loadDataIntoGridView1()
{
tblFrom = new DataTable();
tblFrom.Columns.Add("Col1", typeof(string));
tblFrom.Columns.Add("Col2", typeof(string));
tblTo = tblFrom.Clone();
DataRow tmp;
while (tblFrom.Rows.Count < 20)
{
tmp = tblFrom.NewRow();
tmp["Col1"] = "my row " + tblFrom.Rows.Count;
tmp["Col2"] = "testing";
tblFrom.Rows.Add(tmp);
}
dataGridView1.AutoGenerateColumns = true;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView2.AutoGenerateColumns = true;
dataGridView2.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.DataSource = tblFrom;
dataGridView2.DataSource = tblTo;
}
private void button1_Click(object sender, System.EventArgs e)
{
// if no ROWS selected, get out.
if (dataGridView1.SelectedRows.Count == 0)
return;
foreach( DataGridViewRow vr in dataGridView1.SelectedRows)
{
var currentRow = ((DataRowView)vr.DataBoundItem).Row;
((DataTable)dataGridView2.DataSource).ImportRow(currentRow);
//this will remove the rows you have selected from dataGridView1
dataGridView1.Rows.RemoveAt(vr.Index);
}
}
}
Here, I have a form with the two grids similar to what you had and a single button to "Move" the records (but only one direction), you will have to adjust for one / all / from / to directions.
A few changes. I changed the grid to force selection mode to the entire ROW. This will prevent multiple entries for a single row just because you have multiple columns available. Multi-selection is still possible by dragging between cells OR Ctrl+Click on individual rows. This allows for selecting rows not within the same viewable screen region.
I also just did a create table to force simulating 20 rows of data to force not all rows visible at the same time.
Next the click event... If no ROWS selected, get out... If there ARE, cycle through, but this does ONE PER ROW instead of multiple times if multiple columns. Get the binding data row, import and remove...
You were very close otherwise and did not need the copy-array. I think the overall problem was that of your columns mode selection hitting the same record multiple times. After the remove-at of row 0 for the first cell, row 1 became row 0 and thus copied that, removed and you are now out of a list as there is only one record left to process. Here, we are getting the entire row ONCE, regardless of the column within it selected.
Instead of
ImportRow(currentRow)
use something like this:
table.Rows.Add(currentRow);
Let me know if that works!
Here's a complete snippet that does it.
In this sample I am using 2 BindingList
to achieve your requirements. Upon the user clicking the button, the sample will copy the selected items from dgv1data
to dgv2data
. Then it will remove the items from dgv1data
.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
namespace MoveDataBetweenDataGridView_47454256
{
public partial class Form1 : Form
{
static BindingList<dgventry> dgv1data = new BindingList<dgventry>();
static BindingList<dgventry> dgv2data = new BindingList<dgventry>();
DataGridView dgv1 = new DataGridView();
DataGridView dgv2 = new DataGridView();
Button btn = new Button();
public Form1()
{
InitializeComponent();
InitOurStuff();
for (int i = 0; i < 10; i++)
{
dgv1data.Add(new MoveDataBetweenDataGridView_47454256.dgventry
{
col1 = $"col1_{i}", col2 = $"col2_{i}", col3 = $"col3_{i}"});
}
}
private void InitOurStuff()
{
this.Controls.Add(dgv1);//add the DGV to the form
dgv1.DataSource = dgv1data;//bind our data to it
dgv1.Dock = DockStyle.Left;//place the DGV somewhere on the form
this.Controls.Add(dgv2);//add the DGV to the form
dgv2.DataSource = dgv2data;//bind out data to it
dgv2.Dock = DockStyle.Right;//place the DGV somewhere on the form
this.Controls.Add(btn);//add the Button to the form
btn.Dock = DockStyle.Bottom;//place the Button somewhere on the form
btn.Click += Btn_Click;//give the Button a event handler
}
private void Btn_Click(object sender, EventArgs e)
{
List<int> doneIndices = new List<int>();//this will keep track of the row indices we have dealt with
if (dgv1.SelectedCells.Count > 0)//if something is selected
{
foreach (DataGridViewCell item in dgv1.SelectedCells)//loop through the selected cells
{
if (!doneIndices.Contains(item.OwningRow.Index))//if this cell does not belong to a row index we already processed
{
//we haven't done this row yet
dgv2data.Add((dgventry)item.OwningRow.DataBoundItem);//add the DataBoundItem to the other DGV data
doneIndices.Add(item.OwningRow.Index);//put the current row index in our tracking list
}
else
{
//we have done this row already, move on to the next one
continue;
}
}
}
//remove all the duplicate entries
foreach (dgventry item in dgv2data)
{
dgv1data.Remove(item);
}
}
}
public class dgventry
{
public string col1 { get; set; }
public string col2 { get; set; }
public string col3 { get; set; }
}
}