In my WinForms application I'm populating two DataGridView's like below;
private void PopulateData()
{
//Load data
DataTable dtAll = LoadData();
DataTable dtSelected = dtAll.Clone();
dtAll.PrimaryKey = new DataColumn[] { dtAll.Columns["PK"] };
dtSelected.PrimaryKey = new DataColumn[] { dtSelected.Columns["PK"] };
DataView leftGridView = new DataView(dtAll);
DataView rightGridView = new DataView(dtSelected);
dgvLeft.AutoGenerateColumns = false;
dgvLeft.DataSource = leftGridView;
dgvRight.AutoGenerateColumns = false;
dgvRight.DataSource = rightGridView;
}
Then in some other place I'm exchanging columns between two DataGridView like below;
private void ExchangeData()
{
//Get current row of left grid
DataRow selectedRow = ((DataRowView)dgvLeft.CurrentRow.DataBoundItem).Row;
//Find the row from all data table
DataRow foundRow = dtAll.Rows.Find(selectedRow["PK"].ToString());
if (foundRow == null)
return;
//Exchange row between grids
dtAll.Rows.Remove(foundRow);
dtSelected.ImportRow(foundRow);
}
But only dtAll.Rows.Remove(foundRow); is completing correctly and reflected in the DataGridView but the line dtSelected.ImportRow(foundRow); doesn't add the row to dtSelected. I changed this line to dtSelected.ImportRow(selectedRow); but the result is same. Any thoughts?
In MSDN something catches my attention was;
If the new row violates a Constraint it won’t be added to the data
table.
Note: This question is not related to following SO posts;
DataTable.ImportRow is not adding rows
Why DataTable.Rows.ImportRow doesn't work when passing new created DataRow?
DataTable importRow() into empty table
ImportRow is not working
EDIT: I added the PrimaryKey part, DataView and DataRowCollection.Find method later to incorporate some filtering feature. Without these the code worked as intended.
Another EDIT: I removed the PrimaryKey part from PopulateData method and modified the ExchangeData method as follows;
//Get current row of left grid
DataRow selectedRow = ((DataRowView)dgvLeft.CurrentRow.DataBoundItem).Row;
//Find the row from all data table
int foundRow = dtAll.Rows.IndexOf(selectedRow);
//Exchange row between grids
dtAll.Rows.RemoveAt(foundRow);
dtSelected.ImportRow(selectedRow);
But the issue is same.
OK then it was because of my order of the code to execute. Let me explain.
This was the code I execute for the exchange;
//Exchange row between grids
dtAll.Rows.RemoveAt(foundRow);
dtSelected.ImportRow(selectedRow);
Here the row is first deleted before it's been imported to the dtSelected table. That's why dtSelected never got the row imported whatever the way I tried.
So changing the order of the code fixes my issue;
//Exchange row between grids
dtSelected.ImportRow(selectedRow);
dtAll.Rows.RemoveAt(foundRow);
The fear emotion in Inside Out says a phrase which suites this situation. "My Bad"
Related
I am trying to make a cart and when user select row in product table, enter quantity and click "add to cart" the row will go to the cart table. i can do it but the image column shows the system.byte insread of the image. also when i select new product and add new it to cart, the previous row in cart table got overwritten instead of adding the new one.
public partial class AddToCartForm : Form
{
public AddToCartForm()
{
InitializeComponent();
}
private void AddToCartForm_Load(object sender, EventArgs e)
{
PopulateProductImageDgv("Select * from ProductDetailsTwo", ref dataGridView1);
dataGridView1.MultiSelect = false;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView2.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dataGridView2.RowTemplate.Height = 100;
dataGridView2.AllowUserToAddRows = false;
}
private void btnSaveToCart_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count > 0)
{
DataTable dt = new DataTable();
dt.Columns.Add("Id");
dt.Columns.Add("Name");
dt.Columns.Add("ImageData");
dt.Columns.Add("Qty");
foreach (DataGridViewRow dgvRow in dataGridView1.SelectedRows)
{
dt.Rows.Add(dgvRow.Cells[0].Value, dgvRow.Cells[1].Value, dgvRow.Cells[2].Value, txtqty.Text.ToString());
}
dataGridView2.DataSource = dt;
}
else
{
MessageBox.Show("select something");
}
}
public void PopulateProductImageDgv(string sql, ref DataGridView dg)
{
using (SqlConnection connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["cn"].ConnectionString))
{
connection.Open();
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable table = new DataTable();
//settings for dgv with image
dg.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dg.RowTemplate.Height = 100;
dg.AllowUserToAddRows = false;
da.Fill(table);
dg.DataSource = table;
DataGridViewImageColumn imageColumn = new DataGridViewImageColumn();
imageColumn = (DataGridViewImageColumn)dg.Columns[2];
imageColumn.ImageLayout = DataGridViewImageCellLayout.Stretch;
connection.Close();
}
}
}
}
here is the image. any help is appreaciated thanks
There are a few questions I have, however, to keep it simple, one issue is it appears unnecessary to “recreate” a new DataTable for the second (selected items) grid every time the user clicks the save to cart button.
In addition, the posted code simply “overwrites” the existing data in the “selected” items grid. This is an odd behavior. Example, the user clicks an item, then, clicks the save to cart button. This will save the item in the selected items grid, then the user clicks a different item, then clicks the save button… THEN, using the posted, the previously saved item will go away. I am guessing you do not want this behavior.
Given the above comments and the limited posted code, I suggest a simple solution using two different DataTables for this. One table keeps “all” the items and is used for the user to select from. It obviously starts out populated with all the items. The second table is an “empty” table that is used to hold the items selected by the user.
We could obviously “manually” create the selected items table, however, that requires more work and we would need to make sure that certain columns are maintained. "Cloning" the first table will help finding and copying rows from one table to another. In other words, we want to be able to look in the “selected” items table and see if the currently selected item is already in there. If it is, we don’t want to add a new row, we simply want to update the quantity for the existing selected item.
Given this, the second table’s schema could use the same schema as the first table and only display the columns we want, or in this case… “add” a new quantity column. If we Clone the first tables schema then add the “quantity” column to it, then, searching and copying will be much easier. This will work, and you could do this without manually adding the columns to grid. Meaning that even though we added another column to the second table the row import will still succeed.
A complete example is below, the test data uses an original table with columns “ItemID”, “Description” and “Cost” per unit. The second table, also has those columns and two additional columns “QTY” for the quantity and “TotalCost.” The “TotalCost” column is an “Expression” column that simply multiplies the “QTY” value times the “Cost” value. “TotalCost” values will be updated automatically when the “qty” values changes with each button click. First two global data tables for the grids…
DataTable AllItems;
DataTable SelectedItems;
When the form loads, we fill AllItems with all the items from the DB. Then we “clone” this table schema to the SelectedItems table. Then we add the quantity column to the SelectedItems table. And finally set each grid to the proper DataSource. Something like…
private void Form3_Load(object sender, EventArgs e) {
AllItems = GetDataFromDB();
SelectedItems = AllItems.Clone();
DataColumn qtyCol = new DataColumn();
qtyCol.ColumnName = "QTY";
qtyCol.DataType = typeof(int);
SelectedItems.Columns.Add(qtyCol);
DataColumn totCol = new DataColumn();
totCol.ColumnName = "Tot";
totCol.DataType = typeof(decimal);
totCol.Expression = "Cost * QTY";
SelectedItems.Columns.Add(totCol);
//SetSelectedItemsGridColumns();
dataGridView1.DataSource = AllItems;
dataGridView2.DataSource = SelectedItems;
}
The commented out ‘SetSelectedItemsGridColumns` code is used to customize the second grid’s columns if needed.
With this set up, now it should be relatively simple to “copy” the selected rows from the grid with all the items to the grid with the selected items. It should be noted that whatever field we want to search by/find by, must match the field type from the DB. If you get “type” mismatch errors, check to make sure the “type” defined in the code matches the “type” from the DB. In the example below, I use a “type” of int to uniquely identify each “ItemID” in the DB. This will/may obviously be different for you and you will need to change the code to match the proper type/name you want to use.
First, three variables: newItemID to uniquely identify the selected item. And dataRow which is initialized with the data from each selected row. It is used to find the row in the SelectedItems table and also to update existing rows. Lastly a DataRowView to grab the row from the first grid with all the items.
A simple loop through the selected rows. Grab the selected row and get its unique item id. Parse the quantity value from the quantity text box. Try and get the row from the selected items table. If the returned row is null, then the item is not in the table and we need to add it as a new row. If a row IS returned, then we want to simply add the “quantity” value to the existing row.
private void button1_Click(object sender, EventArgs e) {
int newItemID;
DataRow dataRow;
DataRowView drv;
foreach (DataGridViewRow dgr in dataGridView1.SelectedRows) {
drv = (DataRowView)dgr.DataBoundItem;
newItemID = (int)drv["ItemID"];
int.TryParse(txtQTY.Text.Trim(), out int qty);
dataRow = SelectedItems.AsEnumerable().Where(x => x.Field<int>("ItemID") == newItemID).FirstOrDefault();
if (dataRow != null) {
int tot = (int)dataRow["QTY"] + qty;
dataRow["QTY"] = tot;
}
else {
SelectedItems.ImportRow(drv.Row);
dataRow = SelectedItems.AsEnumerable().Where(x => x.Field<int>("ItemID") == newItemID).FirstOrDefault();
dataRow["QTY"] = qty;
}
}
}
private DataTable GetDataFromDB() {
DataTable dt = new DataTable();
dt.Columns.Add("ItemID", typeof(int));
dt.Columns.Add("Description", typeof(string));
dt.Columns.Add("Cost", typeof(decimal));
Random rand = new Random();
for (int i = 1; i < 10; i++) {
dt.Rows.Add(i, "Item_" + i, rand.NextDouble() * 100);
}
return dt;
}
I Have requirement to fill the datagridview row by row . i.e. if 3rd row is currently selected then data needs to be filled in 3rd row (my query returns always single row ) . same for every row.
some what like
DataTable dt = new DataTable();
dataadapter.Fill(dt);
dataGridView1.Rows[index].DataSource = dt; (just a hunch, but not working)
(instead of ) //dataGridView1.DataSource = dt;
hope that I made my requirement clear to you
Is their any way to accomplish it ....
Thanks in advance....
If your query returns a single row then you just have to fill the columns with correct values from your query:
dataGridView1.Rows[rowIndex].Cells["ColumnName"].Value =
dt.Rows[0]["ColumnNameInDataTable"].ToString();
Your grid will not be bound to the data this way. But it will fill the columns in the current row like you've asked.
I don't think this is possible directly,
You could use a List with a certain amount of empty objects and bind this List to your DataGridView... Then you would have empty Rows to work with. You can then fill single rows of the List with data and update the binding.
I also think you need a BindingSource and not a DataTable in this case...
Something like this:
private List<YourObject> gridData; // List
private BindingSource bindingSource;
public void Init()
{
this.gridData = new List<Anything>();
//prefill list, in this case we want to have 100 empty rows in DataGrid
for (var i = 0; i < 100; i++)
{
this.gridData.Add(new YourObject());
}
this.bindingSource.DataSource = this.gridData;
}
public void UpdateRow(int row)
{
this.gridData[row] = (from .. in select ...).FirstOrDefault(); // your query
}
I am trying to save a new added row in a DataGridView to a database. I can't understand which method to call - either gridview1_UserAddedRow or gridview1_RowsAdded (what if it's just one row?).. So far, I've seen that gridview1_RowsAdded executes every time when the form loads.
The DataGridView is bound using a BindingList.
This is how the gridview1_UserAddedRow looks like:
private void dataGridView1_UserAddedRow(object sender, DataGridViewRowEventArgs e)
{
int lastRow = dataGridView1.Rows.Count - 2;
DataGridViewRow newRow = dataGridView1.Rows[lastRow];
bindinglist.Add(new MyTestClass{ ScheduleId = scheduleId, Name = Convert.ToString(newRow.Cells["Name"].Value),
Value = Convert.ToString(newRow.Cells["Value"].Value), TestId = testId});
}
Unfortunately, this doesn't work and nothing is inserted. Actually, I think this event is called when a new row is clicked. How else can I insert the newly created row in the database?
The code is not updating anything to the database as there is no code to update it.
You need to execute a query to update those new values. You could try using Commands:
http://msdn.microsoft.com/en-us/library/aa984369(v=vs.71).aspx
Or change the list to a DataTable, which allows you to update the values 'automatically' (a bit harder): http://msdn.microsoft.com/en-us/library/z1z2bkx2(v=vs.110).aspx
I would stay away form databinding, but if you can't, you can try this:
// Create a new row
DataRow dr = YourDataSet.Vendors.NewRow(); // Change 'Vendors' with your database table's name
// Add some data to your new row
dr[0] = 124;
// Insert the previous row
YourDataSet.Vendors.Rows.InsertAt(dr, 1); // Change the 1 to your index where you want to insert the data.
Goal:
Having two buttons that should be enable to add or delete data from datagridview. The changes should be make in real time so you should be see the new result.
Problem:
Having problem to display the new result after I have used button add functionality because the result won't display in real time. In order to view the result I have to close and reopen the application in order to view the new result.
Please remember that I don't use a database.
I'm using class table and then I connect the table to the datagridview's datasource.
DataTable table = new DataTable();
table.Columns.Add("a");
table.Columns.Add("b");
foreach (var a in myManagerProduct.GetAllProductList())
{
DataRow row;
row = table.NewRow();
row["a"] = a._articleNumber;
row["b"] = a._name;
dgridStock.Rows.Add(row);
table.Rows.Add(row);
}
dgridStock.DataSource = table;
Try following code snippet to add new row to the grid.
DataView dv = ((dgridStock.BindingContext[dgridStock.DataSource] as CurrencyManager).List as DataView);
DataRowView rowView = dv.AddNew();
rowView["a"] = a._articleNumber;
rowView["b"] = a._name;
rowView.EndEdit();
And make sure dgridStock.DataSource = table; is set only once. After that every addition shouldn't refresh the DataSource property.
As Sanjeevakumar Hiremath said, don't forget EndEdit() command for row or DataTable in general. After finishing all the operations about inserting, updating and deleting data, call DataGridView.Refresh().
I am using C# and .NET 3.5 and have a GridView that I am setting the dataSource programatically in the code-behind page. I have data in a DataTable and then depending on a column value (isValid boolean) of each Row, I create a new row using DataRowView.AddNew() method into 1 of 2 DataViews - dvValid or dvInvalid. I am NOT creating a new DataTable.NewRow to add to the DataView Table. Then I bind the GridView to the appropriate dataView.
There is a problem when I am sorting the GridView. I am having a problem with 1 row not being sorted correctly, all other rows are sorted fine. I debugged my code and found that the DataView.Count is 1 more than the DataView.Table.Rows.Count even though I am calling DataView.Table.AcceptChanges() method. This is strange since the dataTable should have all committed rows and therefore the counts should be the same.
So why are the 2 counts different? A DataView is a subset of the DataTable so should it not have equal or less rows than the DataTable.
When I populate the DataView, should I first create the DataTables rather than creating the DataView directly? Right now, I am directly creating a DataRowView without a dDataTableRow, is this the correct approach?
Thanks for your help.
Code snippet : C#
...
//get the data as DataTable
members = GetMemberDataTable ();
//create views from a new DataTable with no rows
dvValidMembers = new DataView (CreateMembersDT("ValidMembers"));
dvInValidMembers = new DataView (CreateMembersDT("InvalidMembers"));
//iterate thru each row and put into appropriate DataView
foreach (DataRow memberRow in members.Rows)
{
if ((bool)memberRow["isValid"])
//Add to valid members Dview
member = dvValidMembers.AddNew();
else
//add to InValid members Dview
member = dvInvalidMembers.AddNew();
member["memberID"] = memberRow["memID"];
} //foreach
dvInvalidMembers.Table.AcceptChanges();
dvValidMembers.Table.AcceptChanges();
}
private System.Data.DataTable CreateMembersDT ( string tableName)
{
System.Data.DataTable dtMembers = new System.Data.DataTable(tableName);
dtMembers.Columns.Add(new DataColumn("memID", typeof(int)));
return dtMembers;
}
That 1 row that isn't sorting right, could that be the last row?
I think you are missing a DataView.EndEdit():
foreach (DataRow memberRow in members.Rows)
{
DataView dv;
if (...)
//Add to valid members Dview
dv = dvValidMembers;
else
dv = dvInvalidMembers;
member = dv.Addnew();
member["memberID"] = memberRow["memID"];
dv.EndEdit();
}
But I would also like to note that you could probably use 2 Views with a Filter on isValid and then you would only need to point them to the original members table.