BindingSource sees changes, DataTable doesn't - c#

I have recently switched over from Java/RMI to C# / .net, and am working on my first project using databinding to update records in Oracle. On this first form I'm building, I have a detail view for vehicle records (VIN, year/make/model, license plate number, that sort of thing). The first thing I did in terms of writing to the DB was saving updates:
private void btn_saveDesc_Click(object sender, EventArgs e)
{
bool isSaved = false;
hJA_VEHICLEBindingSource.EndEdit();
DataSet1.HJA_VEHICLEDataTable ch =
(DataSet1.HJA_VEHICLEDataTable)dataSet1.HJA_VEHICLE.GetChanges();
if (ch == null)
{
MessageBox.Show("There are no changes to save.");
}
else
{
Service<TVDDataService.IService1>.Use(svcProxy =>
{
isSaved = svcProxy.SaveVehicles(ch);
});
if (isSaved)
{
// update the vehicle in the local dataset
var modifiedRows = from row in dataSet1.HJA_VEHICLE
where row.RowState == DataRowState.Modified
select row;
foreach (DataRow row in modifiedRows)
{
row.Delete();
}
dataSet1.HJA_VEHICLE.Merge(ch);
dataSet1.HJA_VEHICLE.AcceptChanges();
}
if(isSaved)
{
MessageBox.Show("The record has been saved.");
}
else
{
MessageBox.Show("The record could not be saved.");
}
}
}
That all seemed ok, so I moved on to adding new records. I made a button (I saw online where various people had said it was as good or better to make your own than use a binding navigator) and put this in its handler:
DataRowView drv = (DataRowView)hJA_VEHICLEBindingSource.AddNew();
currVeh_id = nextID(); // generate arbitrary ID for the record
drv["VEH_ID"] = currVeh_id;
drv["GRNTE_ID"] = lastSelectedGranteeID; // just to have an initial value
hJA_VEHICLEBindingSource.Filter = "VEH_ID = " + currVeh_id;
So there (above) I'm putting initial values into some required columns (VEH_ID is the PK). Then I ran the app, entered a value in the textbox for VIN, and went to save (same code as above) and this time GetChanges() returned null.
So I tried this in the "add new" button handler, in place of the first thing:
DataSet1.HJA_VEHICLERow newRow =
(DataSet1.HJA_VEHICLERow)dataSet1.HJA_VEHICLE.NewRow();
currVeh_id = nextID();
newRow.VEH_ID = currVeh_id;
newRow.GRNTE_ID = lastSelectedGranteeID;
dataSet1.HJA_VEHICLE.AddHJA_VEHICLERow(newRow);
hJA_VEHICLEBindingSource.Filter = "VEH_ID = " + currVeh_id;
Now I have something really interesting happening.
- I can successfully enter and save data on any number of new records, until I select an existing record. If I move to an existing record and then add a new record, then when I go to save the new record, the values that were explicitly set in code get written to the DB but data entered into the GUI do not "take" for the new record.
- I can successfully change any number of existing records, until I enter a new record. If I add one or more new records, save, and then try to save changes to an existing record, the call to GetChanges() returns null (so again, apparently it is not "seeing" what's been entered through the GUI).
So in both of these cases, the change from old to new, or new to old, appears to introduce some condition that makes the datatable unaware of what was entered into the GUI. But in changing from old to new it only takes selecting an existing record, whereas with changing from new to old, it only breaks after saving (if I do a new record but then abandon it without saving, I can modify existing records without problems).
I added this into the save handler, just prior to the actual save (in a loop because ch is a datatable, but really the code is set up to where you have to either save or abandon the new record before moving on - thus the loop only executes once):
foreach (DataSet1.HJA_VEHICLERow r in ch)
{
DataRowView drv = (DataRowView)hJA_VEHICLEBindingSource.Current;
MessageBox.Show(drv["VIN_ID"].ToString());
MessageBox.Show(r.VEH_ID + "\n" + r.GRNTE_ID + "\n'"
+ r.VIN_ID + "'");
}
Where VIN_ID is the column to which this particular textbox is bound (I tried this with other fields on the form too, to verify it wasn't just something flaky about that one textbox). The first message box (DataRowView from the binding source) shows the vin that I entered into the textbox. The second message box (row from the table returned by GetChanges()) shows the empty string for VIN_ID, although it has the correct (set through code) values for VEH_ID and GRNTE_ID. The same thing happens if I select a different value for GRNTE_ID using the combo box bound to that column; the row from the datatable still has the value that was set through code, "unaware" of the value selected through the GUI.
Why would the datatable and binding source have different values for the same field? And why would the datatable be able to "see" values entered through the GUI only until the user switches from existing to new, or from new to existing?
Thanks.
Angel:
I'm doing that in my Service:
public bool SaveVehicles(DataSet1.HJA_VEHICLEDataTable vehicles)
{
bool saved = false;
if (vehicles != null && !vehicles.HasErrors)
{
HJA_VEHICLETableAdapter ta = new HJA_VEHICLETableAdapter();
int result = ta.Update(vehicles);
saved = (result > 0);
}
return saved;
}
This is called from this block from my original post:
Service<TVDDataService.IService1>.Use(svcProxy =>
{
isSaved = svcProxy.SaveVehicles(ch);
});
Johannes:
The call to EndEdit() is the second line in the save handler (near the top of my post). Should I be calling it somewhere else as well?
Thanks.
Just to clarify: SaveVehicles cannot be the source of the problem, since the problem is appearing before SaveVehicles is ever called. What condition could cause a discrepancy between the BindingSource and the DataTable after EndEdit() has been called and before anything actually writes to the DB?

You have to update your table adapter after use EndEdit(); also you can update your complete DataSet with the follow snippet :
this.BindingSource1.EndEdit();
this.TableAdapter1.Update(this.DataSet1);
Hopes Helps...

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

Related

How to update edits in DataGridView to table

I have bound a DataGridView to an SQL Server table in a .Net 5.0 WinForms project. Displaying the data works well.
I would like to update editions to the database as soon as I move to another row in the DataGridView. But I have not found a way to do this.
The solution presented here seems not to work with an OleDbDataAdapter. The Update method does not accept a DataRow. Examples in DOCS require a DataSet which I try to avoid. Other examples use a button to save changes.
The data gets loaded like this:
var dataAdapter = new OleDbDataAdapter(sqlQueryString, connString);
var dataTable = new DataTable();
dataAdapter.Fill(dataTable); // fill data table from SQL server
var bindingSource = new BindingSource();
bindingSource.PositionChanged += new System.EventHandler(bindingSource_PositionChanged);
bindingSource.DataSource = dataTable; // connect binding source to data table
dataGridView.DataSource = bindingSource; // connect DataGridView to binding source
For the update I finally have tried this:
private void bindingSource_PositionChanged(object sender, EventArgs e)
{
DataRow dataRow = ((DataRowView)((BindingSource)sender).Current).Row;
if (dataRow.RowState == DataRowState.Modified) // this is successful
{
dataAdapter.Update(dataRow); // compile error
}
}
I get the compile error
Cannot convert from 'System.Data.DataRow' to 'System.Data.DataRow[]'.
Any hint is appreciated.
MVVM
In modern programming, there is the tendency to separate the model from the view. This separation makes it easier to change the way that your data is displayed without having to change your model. You can also change parts of the model without having to change the display. It is easier to reuse the model and to unit test it without having to start a forms program.
In WPF this separation between model and view is almost enforced. When using winforms you have to take care that you do not mix them more than needed.
To keep these two separated, adapter code is needed to glue your model to your view. This adapter code is quite often called the viewmodel. the abbreviation of these three is quite often called MVVM. Consider to familiarize yourself with the ideas of MVVM.
Use a BindingList in your DataSource
If you want to separate your model from your view, you need methods to fetch the data that must be displayed from the database, and data to update items.
I don't know what you will be displaying in your DataGridView, but let's assume it is a sequence of Products, something like this:
class Product
{
public int Id {get; set;}
public string ProductCode {get; set;}
public string Name {get; set;}
public string Description {get; set;}
public decimal Price {get; set;}
...
}
You will have methods to fetch the Products that must be displayed, and to Update one Product, or maybe several Products at a time:
IEnumerable<Product> FetchProductsToDisplay(...)
{
// TODO: fetch the products from the database.
}
void UpdateProduct(Product product) {...}
void UpdateProducts(IEnumerable<Product> products) {...}
Implementation is out of scope of this question. By the way, did you notice, that because I put fetching and updating data in separate procedures, I hid where the Products are saved? It can be in an SQL server, but if you want it could also be a CSV or XML file, or even a dictionary, which could be handy for unit tests.
Besides: you can unit tests these methods without using your forms.
Using the visual studio designer you have added the columns and defined which column should show which Product property. You could also have done this in the constructor using property DataGridViewColumn.DataPropertyName
public MyForm()
{
InitializeComponents();
this.columnProductCode.DataPropertyName = nameof(Product.ProductCode);
this.columnName.DataPropertyName = nameof(Product.Name);
...
}
You don't need to set the DataPropertyName for properties that you won't show anyway.
Now to display the products, it is enough to assign the Products to the DataSource of the DataGridView:
var productsToDisplay = this.FetchProductsToDisplay(...);
this.dataGridView1.DataSource = productsToDisplay.ToList();
This will display the products. However, changes that the operator makes: Add / Remove / Edit rows are not updated. If you need this functionality, then the Products need to put in an object that implements IBindingList, like (surprise!) BindingList<Product>:
private BindingList<Product> DisplayedProducts
{
get => (BindingList<Product>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
To Initialize the DataGridView:
private void DisplayProducts()
{
this.DisplayedProducts = new BindingList<Product>(this.FetchProductsToDisplay().ToList());
}
Now whenever the operator makes any change to the DataGridView: Add / Remove rows, or change the Displayed values in a row, these changes are reflected in DisplayedProducts.
If for instance the operator clicks Apply Now to indicate he has finished editing the products:
private void OnButtonApplyNow_Clicked(object sender, ...)
{
var products = this.DisplayedProducts;
// find out which Products are Added / Removed / Changed
this.ProcessEditedProducts(products);
}
Of course you can Add / Remove / Change displayed products programmatically:
void AddProductsToDisplay()
{
Product product = this.DisplayedProducts.AddNew();
this.FillNewProduct(product);
}
Back to your question
Ask yourself: Is it wise to update the database as soon as the position is changed?
If the operator starts typing, then remembers he can copy-paste items, he will stop typing, go to other controls to copy, and then continue editing the cell by pasting. Maybe he goes to other rows to look at information to decide what to put in the cell.
Another scenario: the Descriptions of Product A and Product B need to be exchanged. Think of the operator actions needed for this. When would it be wise to update the database? When are you certain that the operator is content with the new data?
Hence it is not wise to update the database as soon as a row is edited. The operator should explicitly indicate he has finished editing.
private void OnButtonOk_Clicked(object sender, ...)
{
var products = this.DisplayedProducts;
// find out which Products are Added / Removed / Changed
this.ProcessEditedProducts(products);
}
Further improvements
Once you've separated your data (model) from the way this data is displayed (view), using the DataSource, it is quite easy to access the Product that is displayed in the current row or in the selected rows:
Product CurrentProduct => (Product) this.dataGridView1.CurrentRow?.DataBoundItem;
IEnumerable<Product> SelectedProducts = this.dataGridView1.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<Product>();
you can use foreach loop.
private void AddInfo()
{
// flag so we know if there was one dupe
bool updated = false;
// go through every row
foreach (DataGridViewRow row in dgv_Purchase.Rows)
{
// check if there already is a row with the same id
if (row.Cells["Pro_ID"].ToString() == txt_ProID.Text)
{
// update your row
row.Cells["Purchase_Qty"] = txt_Qty.Text;
updated = true;
break; // no need to go any further
}
}
// if not found, so it's a new one
if (!updated)
{
int index = dgv_Purchase.Rows.Add();
dgv_Purchase.Rows[index].Cells["Purchase_Qty"].Value = txt_Qty.Text;
}
}
Finally I've found the 2 missing lines:
private SqlCommandBuilder commandBuilder; // on UserControl level
commandBuilder = new SqlCommandBuilder(dataAdapter); // when loading data
A book has helped me: Michael Schmalz, C# Database Basics, O'Reilly
It is strange that the DOCS reference of SqlDataAdapter doesn't mention the SqlCommandBuilder.
Thanks to everybody who has spent precious time for a New contributor.

Why DataGridView does not show data on 2nd time?

I just need to show updated data in DataGridView. I tried some options but all of them do not work. :-(
Seems that is very tricky control.
Here is the code:
private delegate void displaySearchResultsDgvMethod(DataGridView pDgv, DataTable pTable);
private void displaySearchResultsDgv(DataGridView pDgv, DataTable pTable)
{
if (pDgv.InvokeRequired)
{
pDgv.BeginInvoke(new displaySearchResultsDgvMethod(this.displaySearchResultsDgv), pDgv, pTable);
}
else
{
// Option 1. Smiple direct set
// pDgv.DataSource = pTable;
// option 2. update only when different DataTable object
//bool isDifferent = (pDgv.DataSource == null || !pDgv.DataSource.Equals(pTable));
//if (isDifferent)
// pDgv.DataSource = pTable;
// option 3. use BindingSource
if (pTable == null)
{
bsOrdersList.DataSource = null;
bsOrdersList.ResetBindings(true);
}
else
{
bsOrdersList.DataSource = pTable;
}
}
}
private bool searchOrders(IProgressor pProgressor, object pState)
{
int count = AppDocument.Instance.SearchOrders(this.searchFields, AppDocument.ESearchOptions.EstimateOnly);
if (count > 1000)
{
DialogResult dr = MessageBox.Show(Languages.TranslateFmt("{0} orders found, loading may take time. Do you want to continue?", count),
Languages.Translate("Confirm"), MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
if (dr == System.Windows.Forms.DialogResult.Cancel) return true;
}
/* Option 1-A: reset data before re-loading */ displaySearchResultsDgv(dgvOrdersList, null);
Thread.Sleep(10);
AppDocument.Instance.SearchOrders(this.searchFields, (this.joinToLoaded ? AppDocument.ESearchOptions.Join : AppDocument.ESearchOptions.None));
updateStatusLabs(
Languages.Translate("Displaying..."),
Languages.TranslateFmt("+ {0} orders found...", AppDocument.Instance.SearchResults.Rows.Count));
//displaySearchResults(lvOrdersList);
displaySearchResultsDgv(dgvOrdersList, AppDocument.Instance.SearchResults);
return true;
}
With option# 1* it shows data correctly only on 1st search call. On every other attempts to search orders it is always show empty DataGridView despite the fact that data in AppDocument.Instance.SearchResults is exists (I see number of rows displayes in UI, also in log file).
With option# 2* it shows the data but it is crashing on attempt to scroll data after 2nd search attempt.
With option# 3 works the same way as option 1.
Notes:
Type of AppDocument.Instance.SearchResults is DataTable
There always will be multiple search attempts but all are loading data into the same instance of DataTable object. Sometimes data will be added to DataTable object, sometimes data will replace content of DataTable object. I need DataGridView to show data correctly in all cases.
In this particular case set of columns is always the same
Do not expect to edit data in DataGridView, it should be read-only
Instance of DataTable object which is used here is created manually because there is no db-provider which can work with such data interface.
I would like not to dispose & re-create instance of AppDocument.Instance.SearchResults DataTable object. So, it would be nice to re-use the same instance of DataTable object. Note: I know that if I will dispose AppDocument.Instance.SearchResults DataTable object and create new one - this code will work fine but I would like to avoid this.
It is .NET 3.5
Thank you in advance.
Seems I found very simple solution:
private DataTable dummyTable = new DataTable("DUMMY");
private bool searchOrders(IProgressor pProgressor, object pState)
{
int count = AppDocument.Instance.SearchOrders(this.searchFields, AppDocument.ESearchOptions.EstimateOnly);
if (count > 1000)
{
DialogResult dr = MessageBox.Show(Languages.TranslateFmt("{0} orders found, loading may take time. Do you want to continue?", count),
Languages.Translate("Confirm"), MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
if (dr == System.Windows.Forms.DialogResult.Cancel) return true;
}
displaySearchResultsDgv(dgvOrdersList, this.dummyTable);
Thread.Sleep(10);
AppDocument.Instance.SearchOrders(this.searchFields, (this.joinToLoaded ? AppDocument.ESearchOptions.Join : AppDocument.ESearchOptions.None));
updateStatusLabs(
Languages.Translate("Displaying..."),
Languages.TranslateFmt("+ {0} orders found...", AppDocument.Instance.SearchResults.Rows.Count));
displaySearchResultsDgv(dgvOrdersList, AppDocument.Instance.SearchResults);
return true;
}
So, just create one additional table - dummyTable and then simply switch between these 2 tables - dummyTable and * AppDocument.Instance.SearchResult*.
As I can see it works fine. No exceptions. Data displayed correctly.

Display String in a Database as a Checkbox

I have a Windows Form that was mainly made using the graphical editor. It is connected to a database called, Database1. One of the tables in the database is called Table1, and contains the column CheckBox1. When I connected the form to the database, Database1DataSet.xsd and Database1DataSet.Designer.cs were automatically created.
CheckBox1 can either hold "Yes" or blank (this wasn't my decision). I would like to make a checkbox checked if the value in the CheckBox1 column is "Yes", and unchecked if the value is blank. If I drag a bound checkbox onto the form, it doesn't work because I assume that the values in the column need to be either 1 or 0. So I'm trying to work around this.
In my form, I have the following
// Form Constructor
public myForm()
{
// Initializes all the components in the form
InitializeComponent();
// Change the checkboxes checked state
this.myCheckBox.Checked = myCheckBox_Update();
}
// Method for determining if the checkbox should be checked
private bool myCheckBox_Update()
{
// This SHOULD bring in the current record of the database
DataRowView current = (DataRowView)this.Table1BindingSource.Current;
try
{
// This SHOULD determine if the value in the CheckBox1 field has a value
return current.Row["CheckBox1"].ToString().Length > 0;
}
catch (NullReferenceException ex)
{
MessageBox.Show("NullReferenceException was thrown!", "Error");
return false;
}
}
In the InitializeComponent() function, there is the following
// This line is generated when I drag a bound checkbox to the form
// this.myCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("CheckState", this.Table1BindingSource, "CheckBox1", true));
// For the miscellaneous information about the checkbox
this.myCheckBox.AutoSize = true;
this.myCheckBox.Location = new System.Drawing.Point(3, 3);
this.myCheckBox.Name = "myCheckBox";
this.myCheckBox.Size = new System.Drawing.Size(92, 23);
this.myCheckBox.TabIndex = 0;
this.myCheckBox.Text = "Check Box";
this.myCheckBox.UseVisualStyleBackColor = true;
However, it keeps throwing a NullReferenceException. I'm assuming that this is because this.Table1BindingSource.Current cannot be cast as a DataRowView.
I have looked at several other posts on SO that are somewhat related to this problem (e.g. This post, or this post), but I haven't found anything that has worked so far. The second link's answer doesn't work because I am iterating through the records with this.Table1BindingSource.MoveNext();, and I won't know the index.
Can someone steer me in the right direction? I'd really appreciate it
EDIT: The BindingSource is initialized with this
private System.Windows.Forms.BindingSource Table1BindingSource;
this.Table1BindingSource = new System.Windows.Forms.BindingSource(this.components);
I'll put this in an answer since it's not fit for a comment.
You may also try checking the value for null in your try statement.
try
{
// This SHOULD determine if the value in the CheckBox1 field has a value
object val = current.Row["CheckBox1"];
if (val == null)
{
return false;
}
else
{
return current.Row["CheckBox1"].ToString().Length > 0;
}
}
Or at least set a breakpoint at the if (val == null) statement and make sure val has a value that you would expect.
If by 'blank' you mean a String.Empty, then your code would work as you expect it to, but if blank means null, then you'll have to check for null, you can't call ToString() on a null value.
What I ended up doing is just changing the values in my database from "Yes" and blank to 1 and 0. Then in the InitializeComponent() method, I added the following line (or it might be generated for you if you drag it from the data source panel)
this.myCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("CheckState", this.Table1BindingSource, "CheckBox1", true));

Delete data using 2 DialogForm

hi i have question for remove data from 2 forms dialog
in first dialogform it's contain list of data, and in second dialogform it's contain detail of data and delete button... i already successfully delete data in database but i confused how to remove data from list...
if just select data and delete i know it's can be done with this code
quizzes.RemoveAt(listBoxQuizzes.SelectedIndex);
but problem here in dialogform1 not available button delete, just view details data. so if user want to delete data, he must open dialogform2 (detail data)
i already done delete data in database with this code
Global.deleteData("DELETE FROM Quiz_Occurrences WHERE ID = " + id);
and close detaildataform (dialogform2) by
this.Close();
and move to dialogform1 (listdatabox)
the problem in here, data which just i delete still in there because it's still not remove yet (already delete from database but not remove from list). and need to restart program to see effect of delete data
Update Progress
i changed data to global var, so it's technically i can remove data in dialogform2
this is code (modifier listbox in dialogform1)
int no = 1;
foreach (CQuizOccurrence myQuizOccurrence in Global.quizOccurrences) {
}
if i want to delete it from dialogform1, i can use
Global.quizOccurrences.removeAT(listBoxQuizzes.SelectedIndex);
but if i want to delete it from dialogform2
Global.quizOccurrences.removeAT(.........); //still not have idea how can i reference index
Update solution from #nitin
so first i write in formdialog2
public Frmdialog1 frm_dialog { get; set; }
then i write this in formdialog1
frmdialog2.frm_dialog=this;
then back again to formdialog1 to write
frm_dialog.quizzes.RemoveAt(frm_dialog.listBoxQuizzes.SelectedIndex);
is that right because i get many error
If you are opening second dialog from first one u can Have property in Frmdialog2 like
public Frmdialog1 frm_dialog { get; set; }
After creating object of Frmdialog2 in Frmdialog1 you can set this property as
frmdialog2.frm_dialog=this;
Now u can remove item from this listbox in Frmdialog2 iteself after deleting record from database as
frm_dialog.quizzes.RemoveAt(frm_dialog.listBoxQuizzes.SelectedIndex);
NOTE: Modifier for your listbox should be public
i'm finally be able to do as i want after ask many different question about this topic
first i try to change var to global, so i can remove data in listbox dialogform1 from dialogform2 (i think it's the easiest way)
//in dialog form1
foreach (CQuizOccurrence myQuizOccurrence in Global.quizOccurrences) {
//load data from Global.quizOccurences
}
//call function close to close dialogform1
then in dialogform2, match Global.quizOccurrences data with date&time detail data (using list & foreach)
List<CQuizOccurrence> matchData = new List<CQuizOccurrence>();
foreach (CQuizOccurrence myQuizOccurrence in Global.quizOccurrences)
{
DateTime dtDatabase = (DateTime)myQuizOccurrence.occurred;
string dt = dtDatabase.ToString();
if (dt == dateOccur) {
matchData.Add(myQuizOccurrence);
}
}
foreach (CQuizOccurrence myQuizOccurrence in matchData)
{
Global.quizOccurrences.Remove(myQuizOccurrence);
}
//call function show dialog for formdialog1
form1 could subscibe to the form2's close event.
inside form1
form2 f2dialog = new form2(/*I guess you are passing data here*/);
f2.dialog.Closing += eventhandler;
somewhere else
void eventhandler(object sender, eventargs e)
{
//refresh globaldata since by now you have ran delete query
//rebind or call listbox.items.refresh() or both <-------------this how do you get data from rver? the server is updated but does global know that?
}
then you need to call code to get data from database again. and rebind to data
listbox.datacontext = Global.GetData();//or however this is done
you have to manually reset this eveerytime you change your database
databinding is not as smart as you think it is.

Retrieve Values from asp control in c# code behind

I have looked extensively to find an answer to this question but I only get extremely close. I have a web form that I use to add and edit records. When a record is selected in the gridview, a session variable is set and then used on page load to populate the text fields. If the session variable is not set, the form will be blank and the logic run as a new record. My problem is that I can add a new record successfully - I debugged and checked to make sure the asp controls passed the proper values to the code behind - but I cannot edit a record successfully. For some reason, the code behind file does not retrieve the proper values from the text boxes. Instead, it keeps the original populated values thus defeating the purpose of the edit. I imagine it is a binding issue but I am unsure and have searched upon end. Here is my code behind file:
protected void Page_Load(object sender, EventArgs e)
{
resultOutput.Visible = false;//Output results as to whether or not a record was added successfully is automatically hidden at page load
//Checking to see if session variable has been created
if (Session["editID"] != null)
{
//Create objects to get recipe data
dbCRUD db = new dbCRUD();
Recipe editRecipe = new Recipe();
//Grabbing session ID
var id = Convert.ToInt32(Session["editID"]);
//Call method to retrieve db data
editRecipe = db.SelectRecord(id);
//Populate results to text boxes
recordID.Text = editRecipe.Recipe_ID.ToString();
addName.Text = editRecipe.Name;
addTypeDDL.SelectedValue = editRecipe.Meal;
addDifficultyDDL.SelectedValue = editRecipe.Difficulty;
addCookTime.Text = editRecipe.Cook_Time.ToString();
addDirections.Text = editRecipe.Directions;
//Change Button Text
submitRecord.Text = "Edit Record";
//Change Title Text
addEditTitle.Text = "Edit Recipe";
}
}
protected void submitRecord_Click(object sender, EventArgs e)
{
Recipe recipe = new Recipe();
dbCRUD newRecord = new dbCRUD();
//Variables for execution results
var modified = "";
int returned = 0;
//Creating the recipe Object to pull the values from the form and
//send the recipe object as a parameter to the method containing insert stored procedure
//depending on Add or Edit
//recipe.Recipe_ID = int.Parse(recordID.Text);
recipe.Name = addName.Text.ToString();
recipe.Meal = addTypeDDL.SelectedValue.ToString();
recipe.Difficulty = addDifficultyDDL.SelectedValue.ToString();
recipe.Cook_Time = int.Parse(addCookTime.Text);
recipe.Directions = addDirections.Text.ToString();
//Checking to see if the page is loaded for edit or new addition
if (Session["editID"] != null)
{
recipe.Recipe_ID = Convert.ToInt32(Session["editID"]);
//If recordID exists, recipe will be passed to UpdateRecord method
returned = newRecord.UpdateRecord(recipe);
modified = "has been edited.";
Session.Remove("editID");
}
else
{
//If recordID does not exist, record will be passed to InsertRecord method (new recipe)
returned = newRecord.InsertRecord(recipe);
modified = "added";
}
//Method returns 0 if successful, 1 if sql error, 2 if other error
if (returned == 1)
{
resultOutput.Text = "There was an sql exception";
resultOutput.Visible = true;
}
else if (returned == 2)
{
resultOutput.Text = "There was a non sql exception";
resultOutput.Visible = true;
}
else
{
resultOutput.Text = "\"" + addName.Text + "\" recipe " + modified;
resultOutput.Visible = true;
}
}
Any object passed to my edit method is successful, however, as I mentioned, it does not grab the newly updated text box values.
Did you try checking PostBack property , Your code is loading the data everytime the page is posted back. So when you update the values in the form and hit update button. The Page_Load method is called first and it reloads all the data (replaces your updated values on the form) and then hit the update button event handler. So everytime your old values are being saved.
You may remove the code from page_load method and put it where you are setting the Session["EditId"] value. This will solve your problem.
I would suggest using a static dataset and bind it to the recordsource of the gridview control. Whenever you wanna edit a record update the dataset simultaneously and rebind it to the gridview control....hope that helps:)

Categories