In a C# WinForms project, I'm iterating through a DataGridView's DataSource as a DataTable and I'm doing a check on the source database and determining if a value in one of the columns is valid. If it is a valid value I want to hide it so I only end up with the DGV showing rows with invalid values.
Here's psuedo-code of what I have so far.
private void btnValidate_Click(object sender, EventArgs e)
{
DataTable dt = ((DataTable)dgvMyDataGridView.DataSource);
int intRowIndex;
for (intRowIndex = 0; intRowIndex <= dt.Rows.Count - 1; intRowIndex++)
{
DataRow drValidationRow = dt.Rows[intRowIndex];
string strNewValue = drValidationRow[5].ToString();
// Get the current row's db record ID as a string for the db query
int intCurrDgvRowId = int.Parse(dgvMyDataGridView[0, intRowIndex].Value.ToString());
// Determine if we need to show or hide the DGV's row
bool bNewValueIsValid = <db check for valid strNewValue>
if (bNewValueIsValid)
{
/*
* Hide the DGV row corresponding to the DT row ID
*/
}
}
}
I tried what seems most logical to me:
dgvmyDataGridView.Rows[intRowIndex].Visible = false;
But when I run that I get this error:
System.InvalidOperationException: 'Row associated with the currency manager's position cannot be made invisible.'
I can't just do something like drValidationRow.Visible = false, as there's no Visible property on that, I'm guessing because the row is from the DataTable not the DGV.
How do I accomplish this?
you dont need a counter. You can just refresh dgv if Rows.Count = 0 ?
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 am trying to put a filter on datagridview with ADGV (https://www.nuget.org/packages/ADGV/). However, when I create Datetime value columns by manually the filter does not display filtering by the Year, Month, and Day values.
When the column is created automatically, in runtime this columns shows exactly how to filter.
I want to create these DateTime value columns by manually. What should I do?
private void btnchonLop_Click(object sender, EventArgs e)
{
string TenLop = "Null"; string MaCLB = "Null"; int LichHoc = 3;
adgvHocSinhDuDK.AutoGenerateColumns = false;
adgvHocSinhDuDK.AutoGenerateContextFilters = true;
if (adgvDanhSachLop.SelectedRows.Count > 0)
{
foreach (DataGridViewRow row in adgvDanhSachLop.SelectedRows)
{
TenLop = row.Cells[2].Value.ToString();
MaCLB = row.Cells[7].Value.ToString();
LichHoc = (int)row.Cells[5].Value;
}
DataTable dt = HocSinhServices.LayHocSinhTheoLopDangKy(TenLop, MaCLB, LichHoc);
bdHocSinhDuDieuKien.DataSource = dt; //
adgvHocSinhDuDK.EnableFilter(clNgayNhapHoc); // Enabled Fitleter on NgayNhapHocColumn
}
else return;
}
By manually column
By Auto column
I had the same problem and I've got an answer for you: You have to set up the column ValueType BEFORE adding it to your AdvancedDataGridView.
Somewhere in your designer.cs, you have the declaration of columns and adgv, then you set the parameters of the adgv and then the parameters of the columns (default design in Visual Studio).
In the code, follow this:
First set the parameters of your column and add the ValueType:
example :
this.ContactDate.ValueType = typeof(System.DateTime);
Then add the column in your adgv
All because the filter of DateTime is activated on event "OnColumnAdd": if the ValueType is not DateTime, you won't get the right filter.
I have a data table. I add a row to that data table. And i want to get the values of datagridview's selected row to some text box which data table is bound to. In my datagridview selection changed method i call find method of rows collection of datatable. But it gives me NullReferenceException.
private void dgvRecipeMaterial_SelectionChanged(object sender, EventArgs e)
{
if(dgvRecipeMaterial.SelectedRows.Count > 0 && isDgvRecipeMaterialReady)
{
int rowIndex = dgvRecipeMaterial.SelectedRows[0].Index;
int id = Convert.ToInt32(dgvRecipeMaterial.SelectedRows[0].Cells[0].Value);
object[] data = dtrecetemalzemejoin.Rows[0].ItemArray;
object[] items = dtrecetemalzemejoin.Rows.Find(id).ItemArray;
.
.
.
.
I looked to my id via debugger. Its value is 1166. And when i debug i see there is an item with the id 1166 from the data array. But find method cant find the id 1166. There is no problem if there is more then one record in the data table. What is the problem. data array has the id but find method cant find it.
DataTable.Rows.Find method searching for a rows by columns from DataTable.PrimaryKey property.
Check if your datatable primarykey contain column where your ID value is.
From MSDN: DataTable.PrimaryKey
If PrimaryKey columns did not set. Then before using Find method you set it like this:
dtrecetemalzemejoin.PrimaryKey = {dtrecetemalzemejoin.Columns[0]};
//or may be better will be using a name of column
dtrecetemalzemejoin.PrimaryKey = {dtrecetemalzemejoin.Columns["IDColumn"]};
can you try below code i was also getting same error
private void dgvRecipeMaterial_SelectionChanged(object sender, EventArgs e)
{
if(dgvRecipeMaterial.SelectedRows.Count > 0 && isDgvRecipeMaterialReady)
{
int rowIndex = dgvRecipeMaterial.SelectedRows[0].Index;
string id = Convert.ToString(dgvRecipeMaterial.SelectedRows[0].Cells[0].Value);
object[] data = dtrecetemalzemejoin.Rows[0].ItemArray;
object[] items = dtrecetemalzemejoin.Rows.Find(id).ItemArray;
.
.
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.
I'm trying to filter a deadline column in a datagridview by 2 datetimepickers - startDate and endDate.
datagridview is TaskTable2,
datetimepicker1 is startSchedule,
datetimepicker2 is endSchedule and
deadline in datagridview is deadlineRow
So far I have got the following code which is successfully making the rows invisible which are not between the selected start and end date.
private void scheduleButton_Click(object sender, EventArgs e)
{
DateTime startSchedule = startDate.Value.Date;
DateTime endSchedule = endDate.Value.Date;
if (startSchedule <= endSchedule)// runs foreach loop if startdate and enddate are valid
{
foreach (DataGridViewRow dr in TaskTable2.Rows)// loops through rows of datagridview
{
string deadline = dr.Cells["Deadline"].Value.ToString(); // gets deadline values
DateTime deadlineRow = Convert.ToDateTime(deadline); // converts deadline string to datetime and stores in deadlineRow variable
if (startSchedule <= deadlineRow && deadlineRow <= endSchedule) // filters deadlines that are => startDate and <= endDate
{
dr.Visible = true; // display filtered rows here.
}
else
{
dr.Visible = false; // hide rows that are not beteen start and end date.
}
}
}
else
{
MessageBox.Show("Please ensure Start Date is set before End Date."); // ensures user selects an end date after the start date.
}
}
However, I have a few existing problems:
The application crashes and I get the following error when I select a date range that will display no tasks:
'Row associated with the currency manager's position cannot be made invisible'
I have a print button that is supposed to print the filtered results.
However, it is printing all data stored in the datagridview, even if some rows are visible=false from pressing the schedule button so I'm guessing I need to use a different approach to remove the rows rather than hide them.
The datagridview is bound to an XML file so data can be removed from the datagridview for filtering and printing aslong as they remain in the XML file.
Any help would be greatly appreciated!
Thankyou
I would use the Filter property on the bindingsource for the datagridview. The Filter property allows you to view a subset of the DataSource.
Example from MSDN:
private void PopulateDataViewAndFilter()
{
DataSet set1 = new DataSet();
// Some xml data to populate the DataSet with.
string musicXml =
"<?xml version='1.0' encoding='UTF-8'?>" +
"<music>" +
"<recording><artist>Coldplay</artist><cd>X&Y</cd></recording>" +
"<recording><artist>Dave Matthews</artist><cd>Under the Table and Dreaming</cd></recording>" +
"<recording><artist>Dave Matthews</artist><cd>Live at Red Rocks</cd></recording>" +
"<recording><artist>Natalie Merchant</artist><cd>Tigerlily</cd></recording>" +
"<recording><artist>U2</artist><cd>How to Dismantle an Atomic Bomb</cd></recording>" +
"</music>";
// Read the xml.
StringReader reader = new StringReader(musicXml);
set1.ReadXml(reader);
// Get a DataView of the table contained in the dataset.
DataTableCollection tables = set1.Tables;
DataView view1 = new DataView(tables[0]);
// Create a DataGridView control and add it to the form.
DataGridView datagridview1 = new DataGridView();
datagridview1.AutoGenerateColumns = true;
this.Controls.Add(datagridview1);
// Create a BindingSource and set its DataSource property to
// the DataView.
BindingSource source1 = new BindingSource();
source1.DataSource = view1;
// Set the data source for the DataGridView.
datagridview1.DataSource = source1;
//The Filter string can include Boolean expressions.
source1.Filter = "artist = 'Dave Matthews' OR cd = 'Tigerlily'";
}
I use this type of Filter to show data based on account. For an account, I have a textbox when the user places the account number and I use the TextChanged Event to apply the filter. Then I have a button that is used to remove the Filter from the binding source.
If you want to filter by date you can following instructions in this SO question:
BindingSource Filter by date
Using the filter on a date that is not present should not crash the app, it will just display nothing.
Found a solution to the exception here:
http://discuss.itacumens.com/index.php?topic=16375.0
I added this to my code directly before I attempt to set the row to be not visible. row is my ForEach loop variable. I check to see if its selected and if it is attempt to clear the row and cell selection before setting the visible property.
If gridItems.SelectedRows.Count > 0 AndAlso row.Index = gridItems.SelectedRows(0).Index Then
'fixes dumb exception with row.visible = false
gridItems.ClearSelection()
gridItems.CurrentCell = Nothing
End If
It seems the issue is with making the current row or cell not visible.
I faced exact same problem with exception 'Row associated with the currency manager's position cannot be made invisible'.
dgridView.CurrentCell = null;
dgridView.Rows[i].Visible = false;
Just setting the CurrentCell to null fixed it for me. I haven't checked it further, if it breaks something.