Inline adding row with BindingSource and Validate data - c#

After searching for a solution for some time, I rely on you because I can't get out of it.
I have a DataGridView with the data source linked to a BindingSource.
The BindingSource data source is a DataTable object.
The dgv has the "Allow insertion" option enabled and my goal is to allow the user to enter by adding with submission on the last line and validate the data with RowValidate method.
I want to check, before insert, that a column is filled with data.
To allow insertion, I use the AddingNew method of BindingSource like this:
private void bsAnalisi_AddingNew(object sender, AddingNewEventArgs e)
{
DataGridViewRow dgvRow;
ANALISI_DL AnalisiDL;
//
if (dgvAnalisi.CurrentRow != null)
{
dgvRow = dgvAnalisi.CurrentRow;
//if ((Convert.ToInt32(dgvRow.Cells["ID"].Value) == 0))
if (dgvRow.Cells["ID"].Value == DBNull.Value)
{
Analisi = new ANALISI();
AnalisiDL = new ANALISI_DL();
Analisi.LOTTO = dgvRow.Cells["LOTTO"].Value == null ? string.Empty : dgvRow.Cells["LOTTO"].Value.ToString();
//some other columns
//I call a stored procedure
Analisi.ID = AnalisiDL.InsertAnalisi(Analisi);
//refresh the bindingsource
dgvDataBindings();
}
}
To refresh I use this method :
private void dgvDataBindings()
{
ANALISI_DL AnalisiDL;
//
AnalisiDL = new ANALISI_DL();
bsAnalisi.DataSource = AnalisiDL.GetAllAnalisi();
}
The insert operation goes well but I view a blank row at the bottom of the grid(under the new row).
In addition when I try to refresh the grid with the dgvDataBindings method (by a button) I get the error
System.InvalidOperationException: 'Operation failed. The cell value cannot be committed or uncommitted.'
Is it the correct way to insert a new row ?
I also tried with the CellEndEdit method of the DataGridView but the event fire before of RowValidation event.
EDIT
These are the methods i use to get and insert data and validate :
public DataTable GetAllAnalisi()
{
SqlConnection conn;
SqlDataAdapter sqlDa;
DataTable dt;
//
using (conn = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
conn.Open();
sqlDa = new SqlDataAdapter("SELECT * FROM ANALISI ORDER BY ID", conn);
dt = new DataTable();
sqlDa.Fill(dt);
}
return dt;
}
public int InsertAnalisi(ANALISI pAnalisi)
{
SqlConnection conn;
SqlCommand cmd;
string SQLString;
int result;
//
SQLString = "dbo.SP_INSERT_ANALISI";
using (conn = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
conn.Open();
using (cmd = new SqlCommand(SQLString, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#LOTTO", pAnalisi.LOTTO);
//some other columns
var returnParameter = cmd.Parameters.Add("#ReturnVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
cmd.ExecuteNonQuery();
result = Convert.ToInt32(returnParameter.Value);
}
}
return result;
}
private void dgvAnalisi_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
{
DataGridViewRow row;
row = dgvAnalisi.Rows[e.RowIndex];
if (row.IsNewRow) { return; }
foreach (DataGridViewCell cell in row.Cells)
{
if (cell.OwningColumn.Name == "FK_SEDE")
{
if (cell.Value == null || string.IsNullOrEmpty(cell.Value.ToString()))
{
row.ErrorText = "Error";
e.Cancel = true;
}
}
}
}

Related

How to create a method that can fill several comboboxes one at a time?

I am making an windows form application in C#. In the application, I have a page named category page in which categories are inserted. I wanted to use the categories in another page. So I wrote a method called fillCombo() which generate the inserted categories in combo box in another page. Now I have another combo box in that page and I want the same functionality for that also. How can I do that? Can I make a variable of combo box which will be passed in the method? What is the solution for this problem?
private void fillCombo()
{
Con.Open();
SqlCommand cmd = new SqlCommand("select CatName from CategoryTbl", Con);
SqlDataReader rdr;
rdr = cmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Columns.Add("CatName", typeof(string));
dt.Load(rdr);
CatCb.ValueMember = "catName";
CatCb.DataSource = dt;
Con.Close();
}
We can pass the control as a method parameter like that:
private void FillComboBox(ComboBox combobox)
{
if ( combobox == null ) return; // Or throw new Exception...
Con.Open();
SqlCommand cmd = new SqlCommand("select CatName from CategoryTbl", Con);
SqlDataReader rdr;
rdr = cmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Columns.Add("CatName", typeof(string));
dt.Load(rdr);
combobox.ValueMember = "catName";
combobox.DataSource = dt;
Con.Close();
}
That we call this way:
FillComboBox(myComboBox);
Thus we can fill any combo we want, having its own DataSource to its own DataTable, from the logic of the code provided.
It may be improved and refactored to not execute the query and create a table for each combo:
private const string CategoryColumnName = "CatName";
private DataTable CategoryLookupTable = new DataTable();
private void InitializeCategoryLookupTable()
{
if ( Connection == null ) return; // Or throw new Exception...
Connection.Open();
try
{
using ( var command = new SqlCommand("select CatName from CategoryTbl", Connection) )
using ( var reader = command.ExecuteReader() )
{
CategoryLookupTable.Columns.Add(CategoryColumnName, typeof(string));
CategoryLookupTable.Load(reader);
}
}
finally
{
Connection.Close();
}
}
private void FillFromCategoryLookupTable(ComboBox combobox)
{
if ( combobox == null ) return; // Or throw new Exception...
if ( combobox.DataSource == CategoryLookupTable ) return;
combobox.DataSource = null;
combobox.ValueMember = CategoryColumnName;
combobox.DataSource = CategoryLookupTable;
}
Therefore we will call InitializeCategoryLookupTable somewhere (in the Form Load or Shown event handler for example) and before any call to FillFromCategoryLookupTable :
private void MyForm_Load(object sender, EventArgs e)
{
InitializeCategoryLookupTable();
FillFromCategoryLookupTable(myFirstComboBox);
}
If different source and column name is needed, the refactoring can be done the same manner by passing them as parameters:
private DataTable CreateLookupTable(string nameTable, string nameColumn)
{
if ( Connection == null ) return null;
Connection.Open();
try
{
using ( var command = new SqlCommand($"select {nameColumn} from {nameTable}", Connection) )
using ( var reader = command.ExecuteReader() )
{
var table = new DataTable();
table.Columns.Add(nameColumn, typeof(string));
table.Load(reader);
return table;
}
}
finally
{
Connection.Close();
}
}
private void FillFromLookupTable(ComboBox combobox, DataTable table, string column)
{
...
}

Why do I get the value System.Data.DataRowView? c#

Why do I get the value System.Data.DataRowView? c# + sqlserver
I'm trying to add data to my table but ne system.Data.rowview and I don't know how to do it so that it doesn't come out
Why do I get the value System.Data.DataRowView? c# + sqlserver
This is where I load the items inside the Checklixbox
public void Cargar_Requerimientos(string Id_CR)
{
cn.Open();
SqlCommand cmd = new SqlCommand("SELECT Id_CR, Requisitos, Id_RS FROM Requerimientos WHERE Id_CR =#Id_CR ", cn);
cmd.Parameters.AddWithValue("Id_CR", Id_CR);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
cn.Close();
//DataRow dr = dt.NewRow();
//dr["Requisitos"] = "Seleciona un Requisitos";
// dt.Rows.InsertAt(dr, 0);
///////////////////////////////////////
checkedListBox1.ValueMember = "Id_RS";
checkedListBox1.DisplayMember = "Requisitos";
checkedListBox1.DataSource = dt;
//bool state = true;
// for (int i = 0; i < checkedListBox1.Items.Count; i++)
// checkedListBox1.SetItemCheckState(i, (state ? CheckState.Checked : CheckState.Unchecked));
//dr = dt.NewRow();
enter code here
try
{
//checkedListBox1.DataSource = dt.Columns[0].ToString();
//dt.Columns[0].ToString();
//checkedListBox1.DataSource = dt.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
here I upload the data from combobox1 to checklistbox1
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
// checkedListBox1.Enabled = false;
{
if (comboBox1.SelectedItem.ToString() == null)
{
checkedListBox1.Enabled = true;
}
}
if (comboBox1.SelectedValue.ToString() != null)
{
string Id_CR = comboBox1.SelectedValue.ToString();
Cargar_Requerimientos(Id_CR);
}
Result:
The CheckListBox does not directly support a DataSource, which is why the property is hidden from intellisence.
Usually it is correct to set the DataSource after setting the DisplayMember and ValueMember properties, to avoid multiple refresh calls, but to avoid your issue, you have to set the DataSource property first:
checkedListBox1.DataSource = dt;
checkedListBox1.ValueMember = "Id_RS";
checkedListBox1.DisplayMember = "Requisitos";

keydown Event can't add item in datagridview

When I use the scanner to scan the barcode,
the item will be add in the first row and when I scan the second barcode,
the item will no add in the datagridview but it just adds a row only.
My column in datagridview is productid, ProductName, Description, Stock, UOM, Price
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
DataGridViewRow newRow = new DataGridViewRow();
if (textBox1.Text.Length != 0)
{
conn = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=F:\Database\book1.mdf;Integrated Security=True;Connect Timeout=30");
conn.Open();
SqlDataAdapter adp = new SqlDataAdapter("SELECT productid,ProductName,Description,Stock,UOM,Price from ProductTable where productId='" + textBox1.Text + "'", conn);
DataTable dt = new DataTable();
adp.Fill(dt);
foreach (DataRow item in dt.Rows)
{
int i = dataGridView1.RowCount -1;
dataGridView1.Rows.Insert(i);
dataGridView1.Rows.Add();
dataGridView1.Rows[i].Cells[0].Value = item[0].ToString();
dataGridView1.Rows[i].Cells[1].Value = item[1].ToString();
dataGridView1.Rows[i].Cells[2].Value = item[2].ToString();
dataGridView1.Rows[i].Cells[3].Value = item[3].ToString();
dataGridView1.Rows[i].Cells[4].Value = item[4].ToString();
dataGridView1.Rows[i].Cells[5].Value = item[5].ToString();
}
}
}
}
Page Screenshots:
https://ibb.co/pJ0fnx7
Your approach if your productid is a unique key as it should be, will always be returning only one result, I really dont see the need of the foreach statement here. Moreover every time you open a conn to the database you should be closing it.
My approach with this in mind would be a little different this would be
Public Class clsConn
{
Public List<Data> getSomething()
var SqlConn = new SqlConnection("your connection");
try
{
SqlConn.Open();
string sqlstring = "your sql sentence";
SqlCommand SqlCmd = new SqlCommand(sqlstring, SqlConn);
SqlDataReader reader = SqlCmd.ExecuteReader();
List<Data> dataList = new List<Data>();
if (reader.Read())
{
Data data = new Data();
data.productid = reader[0].ToString(); // this is just an example
dataList.Add(data);
}
return dataList;
}
catch (Exception ex)
{
MessageBox.Show("conexion to DB failed: " + ex.Message);
throw;
}
finally
{
SqlConn.Close();
}
}
}
}
And you should have a public data class that has all the properties you need like this for example
public class Data
{
public string productid { get; set; }
}
To use it, you have to work like this
List<Data> dbData = new List<Data>();
clsConn db = new clsConn();
dbData = db.getSomething();
//I ll leave the foreach but as I said this should be only one result
foreach (var item in DBData)
{
dataGridView1.Rows.Add(item.productid);
}
Your .Insert()-call does not provide the row to insert, and you do not handle the index returned from the Rows.Add()-call.
I have edited your code a bit so that it should work now.
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if ((e.KeyCode != Keys.Enter) || (textBox1.Text.Length == 0))
{
return;
}
conn = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=F:\Database\book1.mdf;Integrated Security=True;Connect Timeout=30");
conn.Open();
SqlDataAdapter adp = new SqlDataAdapter("SELECT productid,ProductName,Description,Stock,UOM,Price from ProductTable where productId='" + textBox1.Text + "'", conn);
DataTable dt = new DataTable();
adp.Fill(dt);
foreach (DataRow item in dt.Rows)
{
int i = dataGridView1.Rows.Add();
DataGridViewRow row = dataGridView1.Rows[i];
row.Cells[0].Value = item[0].ToString();
row.Cells[1].Value = item[1].ToString();
row.Cells[2].Value = item[2].ToString();
row.Cells[3].Value = item[3].ToString();
row.Cells[4].Value = item[4].ToString();
row.Cells[5].Value = item[5].ToString();
}
}
And do not forget to close your database connection. Consider using the using-statement for this.
You should also check this: How to add a new row to datagridview programmatically

Insert formatted values from DataGridView to DataBase

My cell formatting functions is like this :
private void dataGridViewCND_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 2)
{
if (e.Value != null)
{
if (e.Value.ToString().Contains("S010"))
{
e.Value = "BE";
}
else if (e.Value.ToString().Contains("S011"))
{
e.Value = "BI";
}
}
}
}
When I update, the non formatted values are inserted in my database instead of the formatted ones.
Edit :
This is the code I insert in my database with :
private void buttonEnregistrer_Click(object sender, EventArgs e)
{
dataGridViewCND.EndEdit();
dataAdapter.Update(dataTable);
DataBind();
}
This is my DataBind() function
private void DataBind()
{
dataGridViewCND.DataSource = null;
dataTable.Clear();
string query = "SELECT xxxxxxxxxxxx FROM xxxxxxxxxxxx";
SqlConnection con = new SqlConnection(conStringLocal);
con.Open();
SqlCommand command = con.CreateCommand();
command.CommandText = query;
dataAdapter = new SqlDataAdapter(query, con);
commandBuilder = new SqlCommandBuilder(dataAdapter);
dataAdapter.Fill(dataTable);
bindingSource = new BindingSource { DataSource = dataTable };
dataGridViewCND.DataSource = bindingSource;
}
In your dataGridViewCND_CellFormatting method, you are actually formatting values which are going to be displayed on grid view.
This actually doesn't change values in your data source (here dataTable). So data source is holding unformulated data only.
So when you query into database to update it with values of dataTable, database too doesn't get updated with formatted data.
You need to keep updating your dataTable too when you are formatting values on data grid view.
update, dataGridViewCND_CellFormatting like below.
private void dataGridViewCND_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 2)
{
if (e.Value != null)
{
if (e.Value.ToString().Contains("S010"))
{
dataTable.Rows[e.ColumnIndex][e.RowIndex] = "BE";
e.Value = "BE";
}
else if (e.Value.ToString().Contains("S011"))
{
dataTable.Rows[e.ColumnIndex][e.RowIndex] = "BI";
e.Value = "BI";
}
}
}
}

Get data from DB to datagridview based on datagrid values C#

I have a question and I don't seem to get it right in this one... What I've been trying to do is enter a value (id) on a datagridview cell, pressing enter, and filling the adjacents cells with data from the DB (SQL), that matches the id that I entered in the first cell. The questions are:
How do I get the pressed key event on a datagridview?
How do I get the value from that particular cell into, let's an integer?
How do I add up rows from a datatable, without getting the first row deleted in the datagridview? Is there an update method to it?
Sorry if these are basic questions, but I don't seem to find an answer.
Thanks!
Edit: Here's the code I've been trying... what I accomplished with this is that when I press enter, I get a set of new empty columns on the datagrid aside of the columns that I already created
private void dataGridView_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Enter)
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
DataGridViewCell cell = row.Cells[0];
if (cell.Value == null || cell.Value.Equals("") || cell.ColumnIndex == 0)
{
dataGridView1.CurrentRow.cell[0].FormattedValue as int;
int idArticle = Convert.ToInt32(row.Cells[0].Value);
//int idArticle = Convert.ToInt32(dataGridView1.SelectedCells[0].Value);
dataGridView1.AutoGenerateColumns = true;
string constring = #"Data Source=DAN;Initial Catalog=administration;Integrated Security=True ";
using (SqlConnection con = new SqlConnection(constring))
{
using (SqlCommand cmd = new SqlCommand("SELECT id_article, article, quantity, precio FROM articlesSG WHERE id_article=#id_article", con))
{
cmd.Parameters.AddWithValue("id_article", idArticle);
cmd.CommandType = CommandType.Text;
using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
{
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
dataGridView1.DataSource = dt;
}
}
}
}
}
}
}
}
To capture keys try this:
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
var tb =(DataGridViewTextBoxEditingControl)e.Control;
tb.KeyPress += new KeyPressEventHandler(dataGridViewTextBox_KeyPress);
e.Control.KeyPress += new KeyPressEventHandler(dataGridViewTextBox_KeyPress);
}
private void dataGridViewTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
}
To get the current value :
dataGridView1.CurrentRow.Cell[indexorname].FormattedValue as int
To add up rows use DataTable.Merge:

Categories