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.
Related
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.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm pretty new to the C# and while making my first program i'm facing a problem.
So I got 3 windows form (MyForm1; MyForm2 and MyForm3)
MyForm1 has 2 buttons (Available Account & Add a new account)
When i click one of these buttons it opens a new windows form.
In the Add a new Account form I have 2 TextBox (1 for the ID and 1 for the PWD + Button (Save) and i'd like the user to input his ID and PWD and save it so i can re-use it in the Available Account form but i have no clue how to that. I tried different things i saw on YT but nothing seems to works like i would
Thanks for your help <3 (Tell me if you want me to copy/paste some part of the code).
Edit:
Here are the sourcecodes of the mentioned forms.
Form1
Form2
Form3
I deleted all my failed attemps, so they are basics.
From your post it's hard to determine what you're trying to do. So. If you only want to pass values between forms, you could do something like this:
Add new account form:
public static bool AddNewAccount(out int id, out string password)
{
id = 0;
password = "";
AddNewAccountForm f = new AddNewAccountForm();
bool result = (f.ShowModal() == ModalResult.OK);
if(result)
{
id = f.GetId();
password = f.GetPassword();
}
f.Dispose();
return result;
}
and in main form:
int id;
string pass;
if(AddNewAccountForm.AddNewAccount(out id, out pass))
{
//here user clicked OK, so you can save to the database your id and password
}
else
{
//here user clicked Cancel
}
I assumed that there are two buttons on your AddNewAccountForm. One - OK and the other - Cancel. You have to set the modal result for these buttons.
So, how it works?
AddNewAccount method is static method, so you can call it from your main like:
AddNewAccountForm.AddNewAccount()
AddNewAccount method is going to create your form, show it modally and then assign values enetred by user to out parameters.
My code assumes also that your AddAccountForm has methods like:
int GetId()
{
return Convert.ToInt32(idTextBox.Text);
}
string GetPassword()
{
return passwordTextBox.Text;
}
Note that GetId is badly written, I wanted it to be clear. Now that you understand this method, conversion to int should look like that (TryParse is better way to convert string to int):
int GetId()
{
int id;
if(!int.TryParse(idTextBox.Text, out id))
return -1;
else
return id;
}
You can also "group" id and password in some structure. Code would be cleaner. But I don't think you need it now. However, if you are curious you can read about structures here: https://msdn.microsoft.com/en-us/library/aa288471%28v=vs.71%29.aspx?f=255&MSPPError=-2147217396
If you want to store values in database or files:
** Part about good practices and system engeneering **
You should really not save them using AddAccountForm. This class is to create account in your application (just the model) - not to save it. If you want to store these values(id and password) you should pass them to your main form - as I already showed you and then main form should save them - using another class which is responsible for data management. I am not giving any example, because I don't know if you really need it now.
To make your code really reusable, you should keep a strict separation between display (view) and the data.
You didn't mention that you had a database. This lack of mention is a start of this separation. Your problem would be similar if you just have a List of account, or a Dictionary, or maybe a text file containing the items you want to edit in your application.
So let's assume you want to edit a collection of Accounts. You want to be able to add an Account to this collection, or change the data of an existing account in this collection.
This functionality is similar to the functionality of an ICollection<Account>.
So all that Form1 needs to know, is that it holds an ICollection<Account>. Somehow during initialization Form1 loads the Accounts collection. If the operator presses Add, a Form2 opens where he can fill in the required values for a new Account. The operator chooses either OK or Cancel to indicate he want this Account to be added to the collection or not (Using a Save button in the form is not windows standard and a bit unclear, so don't use it).
Add an Account
Code in Form1
private ICollection<Account> existingAccounts;
void OnButtonAdd(object sender, ...)
{
using (var form = new Form2())
{
form. ...// fill any needed values
// show form2 and check if OK or Cancel:
var dlgResult = form.ShowDialog(this);
// only add if OK pressed, otherwise ignore
if (dlgResult == DialogResult.OK)
{
this.existingAccounts.Add(form.Account);
}
}
}
Cond in Form2
In visual studio designer create a Form with a TextBox for the ID and a textbox for the password (give it password properties, so it displays *****)
Add an OK and a Cancel button. Give the DialogResult property of these buttons the proper OK and Cancel value.
Finally add one property to get the typed values:
public Account Account
{
get
{ // extract the values from the display
return new Account()
{
Id = this.TextBoxId.Text,
Pwd = this.TextBoxPwd.Text,
};
}
}
Edit existing Account
You also have a button to edit an existing account. Do you only want to edit the last Added account, or do you want to be able to edit any existing account?
In the latter case you'll have to make something that displays all existing account where operators can select one of them. Probably using a DataGridView, or a BindingSource. You'll probably end up with a function like:
Account GetSelectedAccount() {...}
The Form to edit an existing Account is similar to the form to create a new account. You should really consider using the same form for it.
public Account Account
{
get
{ // extract the values from the display
return new Account()
{
Id = this.TextBoxId.Text,
Pwd = this.TextBoxPwd.Text,
};
}
set
{
this.TextBoxId.Text = value.Id;
this.TextBoxPwd.Text = value.Pwd;
}
}
In form1, upon pressing Edit:
void OnButtonEdit_Click(object sender, ...)
{
using (var form = new FormEdit())
{
Account accountToEdit = this.GetSelectedAccount();
form.Account = accountToEdit;
// or: GetLastAddedAccount if you only want to edit the last added one
var dlgResult = form.ShowDialog(this);
if (dlgResult == DialogResult.OK)
{ // extract the edited Account from the form:
Account editedData = form.Account;
this.UpdateSelectedAccount(editedData);
}
}
}
Like in the examples above I usually decide to have an interface with a property that inserts and extracts Accounts instead of accessing every Account property separately. This allows you to change internals of an Account without having to change all (software) users of this Account
It's all about passing data between forms, So you can use one of following :
set the user input in public string so you can access the strings from other forms by the input form object.
you can pass the user input as constructor parameters and then use the data in your form.
there are also other multiple ways like delegate but i think the 2 previous ways are simple.
I have got a txt file with 10 lines in it, each line is a record with 5 different fields;
Farrell,Jade,Louise,2011/09/13,F
I am using the commas to split record by FamilyName, FirstName, MiddleName, EnrolmentDate and Gender. I want each field to have its own text box then use buttons to look through the different records.
Everything so far is working under the load button which reads the data from the file and puts it into the text boxes using the code below which works but it only shows the first record so i want a buttons to show the next record, previous record, first and last record and also a button to sort the data from A-Z by the family name. Any help on how to go forward would be great! thanks!
private void Load_BT_Click(object sender, EventArgs e)
{
OpenFileDialog filechooser = new OpenFileDialog();
StreamReader filereader = new StreamReader("StudentFile.txt");
String inputrecord = filereader.ReadLine();
string[] inputfields;
if (inputrecord != null)
{
inputfields = inputrecord.Split(',');
FamName_TXT.Text = inputfields[0];
FirstName_TXT.Text = inputfields[1];
MiddleName_TXT.Text = inputfields[2];
Enrolment_txt.Text = inputfields[3];
Gender_TXT.Text = inputfields[4];
}
else
{
MessageBox.Show("End of File");
}
}
I think there are some design issues here, but i will address your immediate concern. The problem is you only read one line. You need to iterate over all the lines in the textfile. I am assuming you want the load button to load all the data at once.
string[] allRecords = filereader.ReadAllLines();
foreach(string inputRecord in allRecords) {
string[] inputfields = inputRecord.Split(',');
//insert the textbox.Text += inputfields[0] + "\n"; etc
}
if you want a single button to resort the data across all the textboxes.
you really should create a class called Person with properties that correspond to your fields and override compareTo so you can sort by last name or maybe use linq to do the sort for you.
you need a list that will host all of these person objects
from there you can populate the textboxes accordingly
Create a sort button that will reorder the list or create a new list and repopulate the textboxes.
1-3 would be done in the load button. The reason for the person class is because you want all the data you read in from the file to be associate with an object. if you try to do the sorting directly from the textboxes as you seem to be trying to do you will run into issues such as how to make sure the data doesnt get jumbled. It certainly may be possible but it is not an elegant way and would be more work in my mind
I'm using C# WPF MVVM. So in XAML there is a listview, which is binded to an object, used to show different information from sql database depending on tab.
For example. I have two forms: one is that shows information and another that is used to input information. How can I automatically update the listview in one form, after new information was entered in another form? Because now I have to switch tabs to get the listview updated.
binding direction for this element should be exposed to TwoWay (Mode=TwoWay)
like this:
x:Name="list"
ItemsSource="{Binding ....... , Path=........., Mode=TwoWay}}" ......
Apart from the default binding, which is one way, you can also configure binding to be two way, one way to source, and so forth. This is done by specifying the Mode property.
OneWay: Causes changes to the source property to automatically update the target property but the source does not get changed
TwoWay: Changes in the source or target automatically cause updates to the other
OneWayToSource: Causes changes to the target property to automatically update the source property but the target does not get changed
OneTime: Causes only the first time change to the source property to automatically update the target property but the source does not get changed and subsequent changes do not affect the target property
you can look this : http://msdn.microsoft.com/en-us/library/ms752347.aspx
After you enter new information into a form, try to invoke your own method, which will update your information into a list view.
So you can use some event eg. DataContentChanged or your update method can be called when u click the button which adds new data into your form.
Example of refresh method should look like this:
public void lbRefresh()
{
//create itemsList for listbox
ArrayList itemsList = new ArrayList();
//count how many information you wana to add
//here I count how many columns I have in dataGrid1
int count = dataGrid1.Columns.Count;
//for cycle to add my strings of columns headers into an itemsList
for (int i = 0; i < count; i++)
{
itemsList.Add(dataGrid1.Columns[i].Header.ToString());
}
//simply refresh my itemsList into my listBox1
listBox1.ItemsSource = itemsList;
}
EDIT: To finish and solve your problem, try to use this snippet of code:
//some btn_Click Event in one window
//(lets say, its your callback " to update" button in datagrid)
private void Button_Click_1(object sender, RoutedEventArgs e)
{
//here you doing somethin
//after your datagrid got updated, try to store the object,
//which u want to send into your eg. listbox
data[0] = data; //my stored data in array
//for better understanding, this method "Button_Click_1" is called from Window1.xaml.cs
//and I want to pass information into my another window Graph1.xaml.cs
//create "newWindow" object onto your another window and send "data" by constuctor
var newWindow = new Graph1(data); //line *
//you can call this if u want to show that window after changes applied
newWindow.Show();
}
After that your Graph1.xaml.cs should look like this:
public partial class Graph1 : Window
{//this method takes over your data u sent by line * into previous method explained
public Graph1(int[]data)
{
InitializeComponent();
//now you can direcly use your "data" or can call another method and pass your data into it
ownListBoxUpdateMethod(data);
}
private void ownListBoxUpdateMethod(int[] data)
{
//update your listbox here and its done ;-)
}
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.