Cant catch errors when editing - c#

I'm kinda new to c# and winforms and need some help.
I've created a dataset and insert it 2 tables "order_lines" and "products". i have a datagridview with columns taken from the table "order_lines" which is an empty table (with no data) .
So in the datagridview i have 3 empty columns: quantity, product (which is a combobox taken from the other data set table products) and a column total which I've created myself (all other columns such as product_id and order_num are invisible).
I'm trying to allow the user to edit and insert data in the datagridview to the columns quantity (insert number) to the column product (choose product from combobox) and the total should be a calculation of the quantity*product_price (according to the product selected from the combobox each product have an id and the price should be taken from the table products according to the product id)
I have 2 problems:
I'm trying to check the user inserted data with the cell_validating event and the data_error
but it doesnt work and instead of getting my error message when the user put invalid data i get an the exception "object cannot be cast from dbnull to other types" which i dont understand why
I don't seem to manage to take the price from the dataset table products and use it in the total column according to the product selected (in the datagridview which show the dataset table "order_lines" theres a hidden column "product_id" also in the dataset table "products" i have the column "product_id" and "product_name" which is the combobox in the datagridview) when a user choose a product i need to get the price.
I hope i managed to explain my question, any ideas will be highly appreciated

This is how I suggest you to do it:
DataTable products;
public Form1()
{
InitializeComponent();
// handle cell changes
dataGridView1.CellValueChanged += dataGridView1_CellValueChanged;
// used for manually raising the ComboBox change
dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;
}
void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// return if the row or column headers were changed
if (e.RowIndex < 0 || e.ColumnIndex < 0)
return;
if (e.ColumnIndex != dataGridView1.Columns["total"].Index)
{
var value = 0;
// get the product id from the ComboBox
var product = dataGridView1.Rows[e.RowIndex].Cells[dataGridView1.Columns["Product"].Index].Value;
// get the quantity
var quantity = dataGridView1.Rows[e.RowIndex].Cells[dataGridView1.Columns["TotalQuantity"].Index].Value.ToString();
if (product != null && !String.IsNullOrEmpty(quantity))
{
value =
int.Parse(quantity) *
int.Parse(products.Select("product_id = " + product.ToString())[0]["product_price"].ToString());
dataGridView1.Rows[e.RowIndex].Cells[dataGridView1.Columns["total"].Index].Value = value;
dataGridView1.Invalidate();
}
}
}
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

Related

Check duplicates before copying rows from one datagrid to another datagrid

Actually I am copying rows from one datagrid to another on DataGridView Double Click Event, and it's going well. The code is here:
//Void App
void AddProducts(int ProdID, string Name, string ProductCode, int RetailPrice, int SalePrice, string BrandName, string Category, int QTY)
{
string[] row = { Convert.ToUInt32(ProdID).ToString(), Name, ProductCode,
Convert.ToInt32(RetailPrice).ToString(), Convert.ToInt32(SalePrice).ToString(), BrandName, Category, Convert.ToInt32(QTY).ToString()};
dgViewProductsToSell.Rows.Add(row);
}
//Code
private void dgViewProducts_MouseDoubleClick(object sender, MouseEventArgs e)
{
string Name1 = this.dgViewProducts.CurrentRow.Cells["Name"].Value.ToString();
int ProdID = Convert.ToInt32(dgViewProducts.CurrentRow.Cells["ProdID"].Value);
string Name = dgViewProducts.CurrentRow.Cells["Name"].Value.ToString();
string ProductCode = dgViewProducts.CurrentRow.Cells["ProductCode"].Value.ToString();
int RetailPrice = Convert.ToInt32(dgViewProducts.CurrentRow.Cells["RetailPrice"].Value);
int SalePrice = Convert.ToInt32(dgViewProducts.CurrentRow.Cells["SalePrice"].Value);
string BrandName = dgViewProducts.CurrentRow.Cells["BrandName"].Value.ToString();
string Category = dgViewProducts.CurrentRow.Cells["Category"].Value.ToString();
int QTY = Convert.ToInt32(dgViewProducts.CurrentRow.Cells["QTY"].Value);
AddProducts(Convert.ToInt32(ProdID), Name, ProductCode, Convert.ToInt32(RetailPrice), Convert.ToInt32(SalePrice), BrandName, Category, Convert.ToInt32(QTY));
}
How I can check duplicate entries before copying the rows? So the 2nd datagridview could avoid duplicate entries.
I'm going to start off with this scrappy test app:
Don't worry that it's a screenshot of code - that's just for information purposes of how it came to be that I generated a datagridview with some data in it. This is how do "do it properly" - you keep your data in a DataTable and you use a grid to show it
The next step: we are going to make a strongly typed dataset, datatable, set its primary key, put it on the form along with a datagridview it is bound to, and we are going to fill it uniquely by double clicking the existing grid. I modeled the existing grid more like you have done. In reality I would make BOTH these grids be based on a strongly typed data table, but I wanted to show you you didn't have to, and my advice for making a strongly typed datatable can fit in with your existing idea of stuffing data into a datagridview directly, until you're ready to change over
Add a new DataSet type item to your project. Right click the project, Add.. New Item:
Give it a nice name. Nothing worse than code full of Form1, DataSet1, GridView3, Button27
When it's added, double click it to open the design surface. Right click the surface and add a new datatable:
Give it a nice name, right click it repeatedly and add columns for all you want, Name, Code, Price etc:
Don't forget to give them a datatype too, so not everything is a string!
You said you want to be unique by name so.. Click in the grey bit next to Name so the row goes all blue, then right click and choose Set primary key
Save it all, close the dataset designer, go to the forms designer, click the VIEW menu, go to OTHER WINDOWS >> DATASOURCES. You can also press Shift+Alt+D
Open the DATA SOURCES panel, find the grid node of your datatable (whatever you called it) and drag it onto the form
A datagridview, connected to your custom datatable has appeared, together with some new things at the bottom. You can delete the bindingnavigator:
(and rename the dataset instance so it has a nicer name than xxxxDataSet1)
That's all the setup we need for now to set up the receiver datatable, and grid. Let's edit the event handler for the mouse double click. I jiggled things around a bit and added another feature as a demonstration, that if the row IS there we can update it, otherwise we add it:
private void _dataGridViewSrc_MouseDoubleClick(object sender, MouseEventArgs e)
{
//my source datagrid is bound to a datatable. I use this to retrieve the current row related
//to the row in the grid that was double clicked
DataRow sourceRow = (_dataGridViewSrc.CurrentRow.DataBoundItem as DataRowView)?.Row;
if(sourceRow == null)
return;
//because we made Name the primary key, the strongly typed datatable has a FindByName method
//we pass it the string Name from the source datatable row (a grid row cell value in your case)
NiceNameDataSet.UniqueProductsRow destRow = _niceNameDataSet.UniqueProducts.FindByName((string)sourceRow["Name"]);
//FindByName returns NULL if the row wasn't found
if(destRow == null)
{
//the row isn't there, so add it, using the data from the source row
_niceNameDataSet.UniqueProducts.AddUniqueProductsRow(
(string)sourceRow["Name"],
(string)sourceRow["Code"],
(double)sourceRow["Price"]
);
} else
{
//the row IS there, how about we update the fields using the row that was just clicked?
//this demonstrated how much nicer it is to work with a strongly typed dataset/datatable
//because all the columns have proper Properties, like row.Code, with proeprt datatypes
//like string and double, rather than having to be referred to with a string name like
//row["Code"] and having to be cast from object to string, double all the time: messy
//and error prone
destRow.Code = (string)sourceRow["Code"];
destRow.Price = (double)sourceRow["Price"];
}
}
I changed the prices and codes a bit to demo this other featuer. Now you can double click either bread row as much as you like and you only get one row in the destination grid, and it changes its values back and forth:
You can create an object which represents each row with properties for each column. For this object you can create an public bool Equals(object obj) method.
With the object you can compare already read objects with newly read objects. Either manually or by using something Contains or similar methods which are part of List<> and Dictionary<> classes.
It all depends on how much data you have and which line you consider to be equal to another line. Is it equal / a collision if the product ID is a duplicate or do all properties also have to match
Each row should have a primary key, usually an ID. I am assuming this is the case for you.
Second assumption: both datagridview controls are bound to datatables/datasets or binding sources.
So, when the user selects a row in the grid, retrieve the underlying datarow. This can be done using the databounditem function. Then you can fetch the ID or primary key.
Then compare the two datatables. This is more efficient and safer than accessing UI controls. There are different ways to check that a record already exists in a datatable, for example the select function. Another option is using LINQ.
Do not try to update the second datagridview directly, instead you add a record to its underlying datatable, and you let the datagridview update itself.
Your code seems to be unsafe. What happens if the user double-clicks on the edges of the grid ? The current row could be -1. You need to add a check.
You should also test what happens if the user sorts the columns in the datagrid or drag the columns to change the display order.
To sum up I strongly advise to use datatables, even if you are not reading from or saving to database.
You can use the following code to remove duplicate entries before copying the datagridview.
Code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
DataTable table = new DataTable();
table.Columns.Add("Name");
table.Columns.Add("Id");
table.Columns.Add("Age");
table.Rows.Add("test1",1001,22);
table.Rows.Add("test1", 1001, 22);
table.Rows.Add("test2", 1002, 23);
table.Rows.Add("test3", 1003, 24);
dataGridView1.DataSource = table;
}
private void dataGridView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
DataTable dt = (DataTable)dataGridView1.DataSource;
dt = dt.DefaultView.ToTable(true);
dataGridView2.DataSource = dt;
}
}
Result:
I don't know if I correctly understand your question but maybe this will help
before adding into my gridview I already checked the possibility of duplicate
//Validate duplicate data b4 adding to gridview
for (int i = 0; i < dataGridView2.Rows.Count; i++)
{
if (textBox1.Text == dataGridView2.Rows[i].Cells[1].Value.ToString())
{
MessageBox.Show("Product already on the list, if you wish to add this product click the product name and edit the quantity", "System Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
textBox1.Text = "";
textBox2.Text = "";
textBox1.Focus();
return;
} else { }
}
then pass the datagridview1(form1) to datagridview2(form2)
List<PurChaseRcpT> cdgv = new List<PurChaseRcpT>();
foreach (DataGridViewRow Item in dataGridView2.Rows)
{
cdgv.Add(new PurChaseRcpT {
Particular = Item.Cells[0].Value.ToString(),
Qty = Item.Cells[2].Value.ToString(),
UM = Item.Cells[3].Value.ToString(),
Price = Item.Cells[4].Value.ToString(),
Total = Item.Cells[5].Value.ToString()
});
}
FrM_SubChange ChangeFrm = new FrM_SubChange();
ChangeFrm.dataGridViewPassed = this.dataGridView2;
ChangeFrm.TempVal = cdgv;
ChangeFrm.Show();
hope it help..

Conditional Page Break On XtraReports Table

I have a grid on my XtraReports and i would like to place in a conditional page break my list consist of oranges, Lemons and Apples and their descriptions. how do i iterate through each row and get a Field value for particular column.
i have tried accessing the detail Report which is my grid table name and Get Current Column Value but that is not what i am looking for because row 1 will have apple and row 2 will have lemons so i want to break each time when row 1 column 1 does not match row 2 column 1
private void xrPageBreak1_BeforePrint_1(object sender,
System.Drawing.Printing.PrintEventArgs e)
{
SetBreaks(sender);
}
private void SetBreaks(object sender)
{
XRPageBreak control = sender as XRPageBreak;
var ItemName =
DetailReport.GetCurrentColumnValue("Item").ToString();
if(ItemName == "Apple")
{
control.Visible = true;
}
}
the code above returns "Apple"

when CSV has more column headers than a database table during import

I am trying to import csv to mssql using c# code on a asp.net application.
the below c# code helps to load the csv file, choose the table to import to
and then a button click event matches columns and populates a gridview (dgvmatchdata)
the gridview has two columns where the left side column lists all the available table headers from db
and right side column lists all the csv headers in a drop down list.
now, there are 3 conditions
1. both table and csv has equal no. of column headers.
2. table has more columns than csv
3. csv has more columns than table.
i have successfully finished the first two scenarios. and now, i am stuck at the 3rd scenario
my approach to this is,
for example lets consider the table as 10 columns and csv has 15 columns.
i wish to create 15 rows in dgvmatchdata and display the 10 table column headers on the left side
on their own labels for each. the equivalent righ side of the dgvmatchdata will have a drop down list
which contains 'ignore this column' + 15 columns headers from the csv. so, the ddl will now have 16 items.
i want to place text box for the remaining 5 rows on the table column side and i want to populate
the text box with dropdown list. selected items.text during dropdown selected item change event.
now after i have successfully got text inside the remaining 5 text boxes i will write a code on a button click event
to alter table in the db and then the next step would simply import the csv data to the altered table flawlessly.
the point where i am seeking help is, i have placed the text box correctly but the text box disappears on ddl.selecteditem changed
due to post back issue and i am unable to get the text inside the text box.
kindly help.
Gridview Code
protected void dgvmatchdata_RowDataBound(object sender, GridViewRowEventArgs e)
{
DataTable dt = (DataTable)Session["importcsv"];
string[] csvcolNames = (from dc in dt.Columns.Cast<DataColumn>() select dc.ColumnName).ToArray();
string tablename = ddltable2.SelectedItem.Text;
string[] dbcolNames = loadDataBaseColumns(tablename);
int dbcol = dbcolNames.Length;
int csvcol = csvcolNames.Length;
if (e.Row.RowType == DataControlRowType.DataRow)
{
//Find the DropDownList in the Row
DropDownList ddlcsvcolumns = (e.Row.FindControl("ddlcsvcolumns") as DropDownList);
ddlcsvcolumns.DataSource = csvcolNames;
ddlcsvcolumns.DataBind();
ddlcsvcolumns.Items.Insert(0, new ListItem("Please select"));
}
for (int i = 0; i < dgvmatchdata.Rows.Count; i++)
{
DropDownList ddlselect = dgvmatchdata.Rows[i].FindControl("ddlcsvcolumns") as DropDownList;
foreach (string col in csvcolNames)
{
string tablcol = ((Label)dgvmatchdata.Rows[i].FindControl("lblcolumns1")).Text;
if (tablcol == col)
{
ddlselect.SelectedItem.Text = col;
ddlselect.Enabled = false;
dgvmatchdata.Rows[i].Cells[1].BackColor = System.Drawing.Color.SpringGreen;
}
}
}
}
Drop Down Selected Index Changed
protected void ddlcsvcolumns_SelectedIndexChanged(object sender, EventArgs e)
{
for (int i = 0; i < dgvmatchdata.Rows.Count; i++)
{
string selectcolumns = ((DropDownList)dgvmatchdata.Rows[i].FindControl("ddlcsvcolumns")).SelectedItem.Text;
Label selectlabel = (dgvmatchdata.Rows[i].FindControl("lblColumns") as Label);
TextBox txtcol = (TextBox)dgvmatchdata.Rows[i].FindControl("txtDynamicText" + i.ToString());
if (selectcolumns.Equals("Please select"))
{
selectlabel.Text = "";
}
else
{
selectlabel.Text = selectcolumns;
}
}
}

C# Winforms DataTable cannot find the row with a specified id

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;
.
.

Update Quantity inside of DataGridView not change the Total

i have a problem. Here is my problem:
I got something like this in my DataGridView:
Product Code || Quantity || Description || SubTotal || Total || ....... (and so on)
SM0001 for the Product Code,
100 for the Quantity,
AS 5 for the Description,
10,000 for the Sub Total, and 1,000,000 for the Total
The above it's correct because 10,000 times 100 we get 1,000,000
I add data to the DataGridView and when the data has been added to the DataGridView, i click edit in that DataGridView, when i change some value on it, it will be changed and updated. But, the problem is when i tried to change the "Quantity" from 100 to 500, it didn't change the Total, the Total suppose to update and change too based on Quantity * Sub Total (because i change the Quantity)
How is it like that?
Here is the code for my problem above when i tried to update it from DataGridView:
private void DataGridViewCalculation(object sender, EventArgs e)
{
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
int _quantity = Convert.ToInt32(this.dataGridView1.Rows[i].Cells[1].Value);
int _subTotal = Convert.ToInt32(this.dataGridView1.Rows[i].Cells[3].Value);
int _total = Convert.ToInt32(_quantity * _subTotal);
this.dataGridView1.Rows[i].Cells[4].Value = _total;
}
dataGridView1.DataSource = null;
dataGridView1.DataSource = _ds.Tables[0];
}
Here is the screenshot of my problem above after and before i change the quantity to 500 from 100:
Screenshot above display when i haven't change the Quantity to 500 (still at 100)
Screenshot above display when i already changed the Quantity to 500, but the Total still the same as Quantity 100
Basically, i want when user click EDIT in DataGridView and made changes in it (let's say user make changes on Quantity in DataGridView, the Total in DataGridView should change too)
EDITED:
dataGridView1.CellValueChanged += new DataGridViewCellEventHandler(this.DataGridViewCalculation);
private void DataGridViewCalculation(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex.Equals(1))
{
int _total = Convert.ToInt32(dataGridView1["Quantity", e.RowIndex].Value) * Convert.ToInt32(dataGridView1["SubTotal", e.RowIndex].Value);
dataGridView1["Total", e.RowIndex].Value = _total.ToString();
}
}
NOTE:
The edited code still not working when i tried to change the Quantity value, the Total still remains same.
You can use CellValueChanged event. This event will be raised when cell value is changed.
Basically what you have to do is,
Check if the Quantity cell is edited by checking column index
Get the value of Sub Total column and Quantity in the same row using RowIndex of the edited cell.
Multiply both and set it to the Total cell.
Sample Code
Please note that this is a sample code, You may have to consider converting values to the required numeric format before performing any operation.
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex.Equals("column Index of Quantity"))
{
double total = Convert.ToDouble(dataGridView1["Subtotal column name", e.RowIndex].Value) * Convert.ToDouble(dataGridView1["Quantity column name", e.RowIndex].Value);
dataGridView1["Total Column name", e.RowIndex].Value = total.ToString();
}
}

Categories