I'm having trouble with a simple table edit in a WinForms Application. I must have missed a step.
I have a DataSet containing a DataTable connected to a database with a SqlDataAdapter. There is a SqlCommandBuilder on the SqlDataAdapter. On the form, there are TextBoxes which are bound to the DataTable. The binding was done in the Designer and its machine-produced statements like this:
this.tbLast.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.belkData, "belk_mem.last", true));
When I fill the row in the DataTable, the values from the database appear in the textboxes, but when I change contents of the TextBox, the changes are apparently not being going to the DataTable. When I try to save a change, both of the following return null:
DataTable dtChanges = dtMem.GetChanges();
DataSet dsChanges = belkData.GetChanges();
What did I forget?
Edit - response to mrlucmorin:
The save is under a button. Code is:
BindingContext[belkData, "belk_mem"].EndCurrentEdit();
try
{
DataSet dsChanges = belkData.GetChanges();
if (dsChanges != null)
{
int nRows = sdaMem.Update(dsChanges);
MessageBox.Show("Row(s) Updated: " + nRows.ToString());
belkData.AcceptChanges();
}
else { MessageBox.Show("Nothing to save.", "No changes"); }
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
I've tried putting in these statements without any change in behavior:
dtMem.AcceptChanges();
belkData.AcceptChanges();
Your missing the DataSourceUpdateMode.OnPropertyChanged when creating Binding instance.
*IF you are using a BindingSource as well: Just do a simple BindingSource.EndEdit() and your TextBox data will be sent over to the DataTable. Example:-
_bsHeader.EndEdit();
if (_dsHeader.HasChanges())
{
DataTable dsInsert = _dsHeader.GetChanges(DataRowState.Added).Copy();
_objDal.Insert(dsInsert);
}
Hope this helps anyone who stumbles here.
Your problem was simply missing 5 letters.... You were missing .last on the dataMember property.
BindingContext[belkData, "belk_mem"].EndCurrentEdit();
Should have been
BindingContext[belkData, "belk_mem.last"].EndCurrentEdit();
Another way to resolve is to use an intermediary BindingSource and call EndEdit() on it before calling GetChanges().
Change your binding to:
BindingSource bs = new BindingSource();
bs.DataSource = belkData;
tbLast.DataBindings.Add("Text", bs, "belk_mem.last", true);
Change your Save button code to
try
{
bs.EndEdit(); // needs to be called before getting changes.
DataSet dsChanges = belkData.GetChanges();
if (dsChanges != null)
{
int nRows = sdaMem.Update(dsChanges);
MessageBox.Show("Row(s) Updated: " + nRows.ToString());
belkData.AcceptChanges();
}
else { MessageBox.Show("Nothing to save.", "No changes"); }
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
You could also just call EndEdit() on the individual rows if you wanted to avoid using the intermediary BindingSource.
belkData.Rows[0].EndEdit();
or if you're dealing with more than one row
foreach( DataRow row in belkData.Rows )
row.EndEdit();
For those struggling to understand why EndEdit() or EndCurrentEdit() needs to be called in the first place. Take a look at the documentation on Row States and Row Versions and BindingSource.EndEdit Method. Until you call EndEdit(), the binding remains in an editing state, and changes are only visible in the Proposed version of the DataTable. Calling EndEdit() moves the changes to the Current version. Only then does the DataTable "have changes". DbDataAdapter's do not look at the Proposed version.
Related
So I have a DataGridView which I am using as a “row selector” on a form, and a bunch of controls are bound to the bindingSource.
One of the bound controls is a ComboBox serving as a lookup which enables status choices for the rows, this is populated from a DataTable with data pulled from the DB.
The population of this box is without any issue.
When a given row is selected from the DGV the form controls display data from the given row as they should, however the “statusComboBox” is not quite playing the game.
If in the DGV, I choose a row that has a different status to one previously selected, it works as it should, however, if I choose a row with the same value to a previously selected row, instead of the box showing the DisplayMember it shows the ValueMember.
IT only seems to occur in the above scenario, where the rows selection only instigates a display response from the bound ComboBox providng a previous selection had a different “Status ID”. What have I dont wrong that would cause this behaviour?
So the form load looks like this
private void ProjectsForm_Load(object sender, EventArgs e)
{
InitBindingSource();
//// bind Selector
//ASMod$ this needs to be 'true' unless you explicitly declare columns
ProjectsDataGridView.AutoGenerateColumns = false;
ProjectsDataGridView.DataSource = ProjectsBindingSource;
GetData();
//Set GeneralStatusBox
Helpers.GeneralStatusInitLookup(statusComboBox, ProjectsBindingSource);
}
The ProjectBindingSource is initialised thus:
private void InitBindingSource()
{
ProjectsBindingSource = new BindingSource();
projectsBindingNavigator.BindingSource = ProjectsBindingSource;
ProjectsBindingSource.PositionChanged += new EventHandler(ProjectsBindingSource_PositionChanged);
}
A ProjectsAddDataBindings procedure, and the contained DataBindings.Add for the ComboBox (executed at the end of a GetData routine that additionally populated ProjectsBindingSource):
ProjectsAddDataBindings();
{
…
this.statusComboBox.DataBindings.Add("Text", ProjectsBindingSource, "GSID");
…
}
After the GetData block the GeneralStatusInitLookup populates the Lookup elements, in a helper class simply because it provides functionality to a number of different forms
public static void GeneralStatusInitLookup(System.Windows.Forms.ComboBox comboBox, BindingSource primaryBindingSource)
{
string statusFilter = "";
statusFilter = Helpers.GetStatusGroupFilter(EndeavourForm.FilterId);
if (statusFilter != "")
{
statusFilter = " WHERE " + statusFilter;
}
//// string statusFilter = ""; //// temp
string sql = "";
sql = "SELECT GSID, ShortName FROM GeneralStatus" + statusFilter + " ORDER BY Pos";
GeneralStatusDataTable = Helpers.Db.GetDataTable(sql);
comboBox.DataSource = GeneralStatusDataTable;
comboBox.DisplayMember = "ShortName";
comboBox.ValueMember = "GSID";
comboBox.DataBindings.Add(new Binding("SelectedValue", primaryBindingSource.DataSource, "GSID"));
}
And the DGV initiated row change is handled like this
private void ProjectsBindingSource_PositionChanged(object sender, EventArgs e)
{
try
{
// Update the database with the user's changes.
UpdateProjects();
statusComboBox.SelectedValue = (int)CurrentDataRowView.Row["GSID"];
}
catch (Exception)
{
}
}
private void UpdateProjects()
{
try
{
ProjectsDataAdapter.Update((DataTable)ProjectsBindingSource.DataSource);
DataHelper.CommitProposedChanges(projectsDataSet);
if (this.projectsDataSet.HasChanges() == true)
{
ProjectsBindingSource.EndEdit();
ProjectsDataAdapter.Update();
}
CurrentDataRowView = (DataRowView)ProjectsBindingSource.Current;
}
catch (InvalidOperationException)
{
throw;
}
catch (Exception)
{
throw;
}
}
Anyway I hope I haven't swamped readers with to much code, but frankly I cant see where this is going wrong. So any help would be greatly appreciated.
This was a simple solution in the end. Both the GeneralStatusInitLookup() and the ProjectsAddDataBindings() blocks made use of DataBindings.Add ... For the lookup table this was fine, but with the binding to the main table; the later, I had used "Text" as the propertyName parameter.
I have a very basic question I want to update DataGridView using this code
private void updateDGV1_Click(object sender, EventArgs e)
{
SQLiteConnection sqliteCon = new SQLiteConnection(dbConnectionString);
// open connection to database
try
{
cmbl1 = new SQLiteCommandBuilder(datadp1);
datadp1.Update(ds1, "PlotDetails");
MessageBox.Show("Information Updated", "Update", MessageBoxButtons.OK, MessageBoxIcon.Information);
load_table();
AutoCompleteSizeSearch();
AutoCompletePlotSearch();
AutoCompleteOwnerSearch();
AutoCompleteLocatoinSearch();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
After search for a result using this code
private void plots_txt_TextChanged(object sender, EventArgs e)
{
DataView dv = new DataView(dt1);
dv.RowFilter = string.Format("D_ID LIKE '%{0}' AND Area LIKE '%{1}' AND Cat LIKE '%{2}' AND PBS LIKE '%{3}%' AND Name LIKE '%{4}%' AND Size LIKE '%{5}%' AND Location LIKE '%{6}%' AND PlotNo LIKE '%{7}%'", dids_combo.Text, areacs_txt.Text, categorycs_txt.Text, phblses_txt.Text, owners_txt.Text, sizes_txt.Text, locations_txt.Text, plots_txt.Text);
dataGridView1.DataSource = dv;
}
After getting result of search I couldn't been able to update searched result.
updateDGV1_Click works fine on the whole DGV but not on Searched result like in below image
After search,result not updating
I would actually recommend using the TableAdapter to communicate with your database instead of generating a connection in that fashion. This will also allow you to easily update your DataGridView with the contents of the TableAdpater Query (Search) that you want to preform. I wish I could go into more detail but I'm in a hurry at the moment I will provide links explaining this better below.
https://msdn.microsoft.com/en-us/library/bz9tthwx.aspx
I personally use TableAdapters to connect databases in my projects, but I have found a solution that may also allow you to keep your code the way it is for the most part.
http://www.codeproject.com/Articles/14249/How-to-populate-DataGridView-GridView-with-SQL-sta
What you will need to do when you want to preform a search on the current working DataSet. This code is an example I haven't tested it.
string conn_str = "Data Source=dbServer;Initial Catalog=testDB;Integrated Security=True";
string sql_str = “select * from table1”;
SqlDataAdapter data_adapter = new SqlDataAdapter(sql_str, conn_str);
SqlCommandBuilder cmd_builder = new SqlCommandBuilder(data_adapter);
// Populate a new data table and bind it to the BindingSource.
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
// This line populates our new table with the data from our sql query
data_adapter.Fill(table);
db_binding_source.DataSource = table;
// Resize the DataGridView columns to fit the newly loaded content.
data_grid_view.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader);
// you can make it grid readonly.
data_grid_view.ReadOnly = true;
// finally bind the data to the grid
data_grid_view.DataSource = db_binding_source;
Here is also another answer on SO similar to what you're asking
How do I Update/Reload DataGridView BindingSource?
I have populated the value for a datatable by using the value in a database. The data willl get populated into database on a button click. Can anybody tell how to check the value in the datatable which is populated.
I am using visual studio and coding in c#
The code for populating the datatable is as shown below :
protected void Button1_Click(object sender, EventArgs e)
{
try
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
conn.Open();
var sql = #"select scriptname,accnum,Quantity,price from transac where transactio = 'Sell'";
var dataAdapter = new System.Data.SqlClient.SqlDataAdapter(sql, conn);
var dataTablesell = new DataTable();
dataAdapter.Fill(dataTablesell);
}
catch (System.Data.SqlClient.SqlException sqlEx)
{
Response.Write("error" + sqlEx.ToString());
}
catch (Exception ex)
{
Response.Write("error" + ex.ToString());
}
}
DataTable has a very nice DebuggerVisualizer. Set a breakpoint after dataAdapter.Fill(dataTablesell); line; when the breakpoint is hit, hover cursor over dataTablesell to open a debug view and click magnifier button (O-) to open visualizer
You can iterate through rows of DataTable to get the value in each row for given columns.
foreach(DataRow row in dataTablesell.Rows)
{
Debug.WriteLine(row["scriptname"].ToString()); //use cell value where you want
Debug.WriteLine(row["accnum"].ToString()); //use cell value where you want
}
You can also bind the DataTable to DataGridView to see the rows in the DataTable. This MSDN article How to: Bind Data to the Windows Forms DataGridView Control explains how you would do that.
got the answer...
Instead of using Console.WriteLine as Adil has said, I used Response.Write, The code is
foreach (DataRow row in dataTablesell.Rows)
{
Response.Write(row["scriptname"].ToString());
Response.Write(row["accnum"].ToString());
}
I am trying to update a record in .Net. I can't figure it out how to do it. I need to specify that I have a DataGridView that displays all the records, and when I click a cell, it shows in separate TextBoxes the content of all columns. So that is I am trying to do, when I modify the TextBoxes and click the Button for Update, to update the row which contains the clicked cell.
I have tried to do this
private void button2_Click(object sender, EventArgs e)
{
DataRow[] row_update = ds.Tables["Plane"].Select("airline_id = " + aidbox.Text);
try
{
row_update["airline_id"] = int.Parse(aidbox.Text);
row_update["plane_id"] = int.Parse(pid_box.Text);
row_update["name"] = name_box.Text;
row_update["model"] = model_box.Text;
row_update["f_seats"] = int.Parse(fc_box.Text);
row_update["s_seats"] = int.Parse(sc_box.Text);
row_update["b_seats"] = int.Parse(bs_box.Text);
row_update["p_weight"] = float.Parse(weight_box.Text);
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
try
{
builder = new SqlCommandBuilder(data_adapter);
data_adapter.UpdateCommand = builder.GetUpdateCommand();
data_adapter.Update(ds, "Plane");
}
catch (SqlException ex) { MessageBox.Show(ex.Message); }
}
}
but I get this error Table doesn't have a primary key. in this line DataRow row_update = ds.Tables["Plane"].Rows.Find(aidbox.Text); Here I am trying to find the row that has the id in the aidbox (because I assume that the id is unique and never changes only the other values).
Can anyone help me please with this
DataRows.Find method Gets a specified DataRow using the PrimaryKey index
If you cannot define a primary key for the datatable then could use DataTable.Select as it accepts any column in the filter expression
DataRowCollection.Find requires a primary key on the DataTable. Set the PrimaryKey property by creating an array of DataColumns. See the MSDN documentation. Or just use Select instead.
Your previous find() may work through this code,
DataColumn[] temp = new DataColumn[1];
temp[0] = ds.Tables["Plane"].Columns["airline_id"];
ds.Tables["Plane"].PrimaryKey = temp;
DataRow row_update = ds.Tables["Plane"].Rows.Find(aidbox.Text);
I have binded datagridview with datatable (Growns). My main goal is, that user can work with datagridview (dataGridView1), filling and updating data and when button SAVE is clicked, all data would be saved into datatable, because I need it for further work.
Everything works fine, exept saving data into datatable. What am I doing wrong?
Here is my code:
private void Form2_Load(object sender, EventArgs e) {
// TODO: This line of code loads data into the 'tekmovalecDataSet.Odrasli' table. You can move, or remove it, as needed.
this.grownsTableAdapter.Fill(this.competitorDataSet.Odrasli);
}
private void buttonSave_Click(object sender, EventArgs e) {
if (EmptySpace())
{
CompetitorDataSet.OdrasliRow newGrownsRow = competitorDataSet.Growns.NewGrownsRow();
newGrownsRow.StN = textStN.Text;
newGrownsRow.Name = textN.Text;
newGrownsRow.Surname = textSN.Text;
newGrownsRow.Club = textC.Text;
newGrownsRow.YBirth = textYB.Text;
competitorDataSet.Growns.Rows.Add(OdrasliNova);
competitorDataSet.Growns.AcceptChanges();
this.dataGridView1.DataSource = competitorDataSet.Growns;
this.Validate();
this.grownsBindingSource.EndEdit();
if (dataGridView1.BindingContext[competitorDataSet.Growns] != null)
{
dataGridView1.BindingContext[competitorDataSet.Growns].EndCurrentEdit();
}
this.grownsTableAdapter.Update(competitorDataSet.Odrasli);
this.grownsTableAdapter.Adapter.AcceptChangesDuringUpdate = true;
}
else
{
MessageBox.Show("Fill ALL data about competitor!");
}
}
P.S.: When I manually fill datatable, on form open datagridview is filled, so datatable and datagridview are connected I suppose...
P.S.2.: bool EmptySpace works fine.
When you set this.Update(competitorDataSet.Odrasli); the TableAdapter updates the changes from DataTable (news, deleted, updated rows) to the database.
Since you call competitorDataSet.Growns.AcceptChanges(); before TableAdapter.Update, all changes in the table are already accepted and TableAdapter has nothing to update.
So just remove
competitorDataSet.Growns.AcceptChanges();
Also, if you set this.grownsTableAdapter.Adapter.AcceptChangesDuringUpdate = true before grownsTableAdapter.Update(competitorDataSet.Odrasli);, the changes will be accepted and so you don't need to accept changes yourself (and it seems to me that default value is True so I am not sure this line is required)
You are not editing the data with the datagridview, you are changing the dataset using the textboxes, I think this is your example with the manual fill...
I will presume that the code you want to use to update the database begins at this line:
this.dataGridView1.DataSource = competitorDataSet.Growns;
I suspect your problem is in the following code block (explanations in code comments):
//why rebind the datagridview?
//this line should be removed
this.dataGridView1.DataSource = competitorDataSet.Growns;
//why call this here? validation should be done prior
//to adding the new row to the datatable
//this line should be removed
this.Validate();
this.grownsBindingSource.EndEdit();
if (dataGridView1.BindingContext[competitorDataSet.Growns] != null)
{
dataGridView1.BindingContext[competitorDataSet.Growns].EndCurrentEdit();
}
//reverse the order of these 2 lines
this.grownsTableAdapter.Update(competitorDataSet.Odrasli);
this.grownsTableAdapter.Adapter.AcceptChangesDuringUpdate = true;
/* like this:
this.grownsTableAdapter.Adapter.AcceptChangesDuringUpdate = true;
this.grownsTableAdapter.Update(competitorDataSet.Odrasli);
*/
If this does not solve your problem, please post the binding code for your datagriview.