C# refreshing textbox from BindingSource - c#

I am having difficulty refreshing windows forms controls that are using a BindingSource object. We have a CAB/MVP/SCSF client that I (actually “we” since it is a team effort) are developing that will interact with WCF services running on a remote server. (This is our first attempt at this, so we are in a learning mode). One of the calls (from the Presenter) to the service returns a DataSet that contains 3 DataTables, named “Contract”, “Loan” and “Terms”. Each table contains just one row. When the service returns the dataset, we store it in the SmartPart/View in a class member variable, by calling a function in the view called BindData() and passing the dataset in to the view from the presenter class;
private System.Data.DataSet _ds = null;
public void BindData(System.Data.DataSet ds)
{
string sErr = "";
try
{
_ds = ds; // save to private member variable
// more code goes down here
}
}
We are trying to bind each of the three DataTables to an assortment of Windows Forms TextBoxes, MaskedEditBoxes, and Infragistics UltraComboEditor Dropdown comboboxes We created three BindingSource objects, one for each DataTable using the VS2008 IDE.
private System.Windows.Forms.BindingSource bindsrcContract;
private System.Windows.Forms.BindingSource bindsrcLoan;
private System.Windows.Forms.BindingSource bindsrcTerms;
We are binding the values like this
if (bindsrcContract.DataSource == null)
{
bindsrcContract.DataSource = _ds;
bindsrcContract.DataMember = “contract”;
txtContract.DataBindings.Add(new Binding("Text", bindsrcContract, "contract_id", true));
txtLateFeeAmt.DataBindings.Add(new Binding("Text", bindsrcContract, "fee_code", true));
txtPrePayPenalty.DataBindings.Add(new Binding("Text", bindsrcContract, "prepay_penalty", true));
txtLateFeeDays.DataBindings.Add(new Binding("Text", bindsrcContract, "late_days", true));
}
if (bindsrcLoan.DataSource == null)
{
bindsrcLoan.DataSource = _ds;
bindsrcLoan.DataMember = “loan”;
mskRecvDate.DataBindings.Add(new Binding("Text", bindsrcLoan, "receive_date", true));
cmboDocsRcvd.DataBindings.Add(new Binding("Value", bindsrcLoan, "docs", true));
}
This works when we do the first read from the service and get a dataset back. The information is displayed on the form's controls, we can update it using the form, and then “save” it by passing the changed values back to the WCF service.
Here is our problem. If we select a different loan key and make the same call to the service and get a new DataSet, again with 3 tables with one row each, the controls (textboxes, masked edit boxes, etc.) are not being updated with the new information. Note that the smartPart/View is not closed or anything, but remains loaded in between calls to the service. On the second call we are not rebinding the calls, but simply trying to get the data to refresh from the updated DataSet.
We have tried everything we can think of, but clearly we are missing something. This is our first attempt at using the BindingSource control. We have tried
bindsrcContract.ResetBindings(false);
and
bindsrcContract.ResetBindings(true);
and
bindsrcContract.RaiseListChangedEvents = true;
and
for (int i = 0; i < bindsrcContract.Count; i++)
{
bindsrcContract.ResetItem(i);
}
As well as resetting the DataMember property again.
bindsrcContract.DataMember = ”Contract”;
We’ve looked at a lot of examples. Many examples make reference to the BindingNavigator but since the DataTables only have one row, we did not think we needed that. There are a lot of examples for grids, but we’re not using one here. Can anyone please point out where we are going wrong, or point us to resources that will provide some more information?
We’re using VisualStudio 2008, C# and .Net 2.0, XP client, W2K3 server.
Thanks in advance
wes

I was having a similar issue today and found this works.
private void btnCancel_Click(object sender, EventArgs e)
{
this.MyTable.RejectChanges();
this.txtMyBoundTextBox.DataBindings[0].ReadValue();
this.EditState = EditStates.NotEditting;
}

The underlying problem in both your questions is that the Binding-Manager keeps the links to the original objects.
When you assign _ds as the DataSource, the Binding-Manager analyzes the DataSet and acts accordingly. If you assign some other DataSet to _ds, the Binding-Manager has no way of knowing this. It still has the reference on the original DataSet-object. So this explains why you have to reset the DataSource-property to the new DataSet.
It also explains why the removing and adding of the table doesn't lead to your expected result. Again, the Binding-Manager holds the reference to the old table (or the first row in that table). The new table is never bound. Also in this case the reassigning of _ds does not help, because _ds points to the same DataSet-object as before. The Binding-Manager is smart enough to notice that it's the same object and does no rebinding action.
You either have to modify the contents of your bound objects (which fires a PropertyChanged-Event to which the Binding-Manager subscribes) or you have to trigger a complete rebind by assigning a different object to the DataSource-property.
This is a simplified description of what actually happens, but I hope it's enough to explain and solve your problem. Unfortunately I have yet to find a comprehensive explanation of WinForms databinding on the web (or elsewhere).

Failing all else, you can reassign the DataSource every time you receive a new dataset, doing something like this:
bindsrcContract.DataSource = typeof(System.Data.DataSet);
bindsrcContract.DataSource = _ds;
(Also, initializing DataMember first and then DataSource will give you better performance.)

Wes, I'm very glad I could help. I still remember an issue very similar to yours taking me weeks to figure out in the wild...
Regarding your questions, here's all I know:
If you set the DataSource first, then the DataMember, your data source will be scanned twice, since setting the DataMember subsequently changes the existing (valid) binding. If you do it the other way around, setting DataMember first (with DataSource being null or better, typeof(YourData)), binding only takes place once when you set the DataSource.
I think you can apply the same solution here. Instead of just
bindsrcContract.DataSource = _ds;
in your last line, you should write
bindsrcContract.DataSource = typeof(System.Data.DataSet);
bindsrcContract.DataSource = _ds;
Sorry to disappoint, but I learned all I know about data binding from MSDN as well as trial-and-error. It was quite painful. Hopefully someone else can chime in with a useful link or two.

try combination :
bindingsource.EndEdit() // writting data to underlying source
bindingSource.ResetBindings(false) // force controls to reread data from bindingSource
use this whenever you write something to controls.

Related

Is it possible to access DataTable in DataGridView.DataSource so I can use DataTable.Copy()?

I'm attempting to copy DataTable objects so I can store them in a linked list. I had planned to fetch these DataTables from a DataGridView object and use the Copy() method for DataTable.
These DataGridViews contain data retrieved from a MySql database and I am presenting them to the user. If the user wants make a change to the data, I would like the datagridview to reflect the changes (however many the user makes) and then the user can hit a submit button to update changes in the db. The linked list of DataTables serves the purpose of allowing the user to revert some changes that haven't been updated and to display the correct "commit" data on the DataGridView.
Unfortunately, the DataSource property for a DataGridView holds an object and not System.Data.DataTable. Looking at the debugger, the type for the DataSource is object (System.Data.DataTable) and none of the properties allow me to access the entire DataTable directly.
Unless I'm missing something, is there anyway I can access this DataTable directly so I can use DataTable.Copy()? Perhaps some way to make an object enumerable and copy each property of object (System.Data.DataTable) to a DataTable object? I already wrote some code to copy the rows and columns of a DataGridView and I tried storing DataGridViews in a linked list, but that didn't work.
Not much sample code but here's what I had in mind.
private LinkedList<DataTable> dataTableChanges = new LinkedList<DataTable>();
public void UserFilterData()
{
// some code here
dataTableChanges.AddLast((MyDataGridView.DataSource.Copy()); // DataSource is an obj. Can't call Copy()
}
public void BackButton(object sender, EventArgs e)
{
if (dataTableChanges.Count >= 2 )
{
dataTableChanges.RemoveLast();
MyDataGridView.DataSource = dataTableChanges.Last.Value;
}
else
{
MessageBox.Show("No prior changes to revert to.");
}
}
DataGridView.DataSource has object type because DataGridView can use different sources: List, DataTable, BindingSource. since you know exactly what is your DataSource, make a type conversion:
(MyDataGridView.DataSource as DataTable).Copy()

Cascading databound comboboxes

I'm new here this is my first question and I beforehand apologize for any lack of information, search or tags in the post or anything at all.
I'm writing a pretty simple winforms program, and I'm having problems with cascading combo boxes, pretty much like country>Estate>City I'm new to this, I'm using Entity Framework and I'm getting by with something like this:
private void Form_CadAnimal_Load(object sender, EventArgs e)
{
using (DbClinvetEntities ctx = new DbClinvetEntities())
{
cmb_especie.DataSource = ctx.Especie;
cmb_especie.DisplayMember = "Nome";
cmb_especie.ValueMember = "EspecieID"
cmb_raca.DataSource = ctx.raca;
cmb_raca.DisplayMember = "Nome";
cmb_raca.ValueMember = "RacaID";
}
}
So far so good all is going ok, I get them to be populated, I've found countless posts on the matter, but perhaps for lack of knowledge I could not relate them to my situation.
now all I would like to do is filter cmb_Raca by the value selected in cmb_Especie, just to make it clearer Raca table has a foreign key on Especie table, pretty much like you would expect country>estate to be.
I've found posts where a new context is generated on SelectedIndexChanged and a whole new binding is done, but I wonder if there is a way to make this work without making further calls to the Db, any way to take the list of objects and work them offline after the first access?
Sorry if this is anyhow unclear, I'll glady edit the post to provide further information if necessary.
try using the SelectedIndex as your query select command.you can build a module for talking to the database that takes the SelectedIndex.ToString() as a parameter to build your Query and fill data to your form based on that. the Combobox doesn't necessarily have to be databound when it is used that way.

How can I set ComboBox text to a value not in DataSource (C#)?

I have multiple DropDown-style ComboBoxes on a form in which the user is able to specify which units are associated with a value (think meters, feet, etc.). To do this, I use an application string collection in Settings.Settings with some default unit types which is bound to the DataSource of the ComboBox as follows:
this.cboUnit.DataSource =
(System.Collections.Specialized.StringCollection)Properties.Settings.Default.Units;
The user is able to successfully pick a unit or specify a new one.
Now, when I save this.cboUnit.Text into some XML profile, I am able to handle both cases. Opening the XML profile and performing this.cboUnit.Text = "NonExistentUnit"; will fail and cause the first item in the collection to be selected instead.
How can I modify the ComboBox to support the kind of behavior I'm asking, without adding the item to the ComboBox or modifying the DataSource itself?
There was a solution suggested in Using ComboBox.Text when combobox is tied to DataSource but I couldn't find official documentation to support this behavior. For that reason I prefer to avoid it.
I could not find any valid way to insert items into a bound DataSource. So instead I ended up doing the following:
BindingSource bindingSource = new BindingSource((System.Collections.Specialized.StringCollection)Properties.Settings.Default.Units, "");
if(!bindingSource.Contains(someSavedValue))
{
bindingSource.Insert(0, someSavedValue));
}
this.cboUnit.DataSource = bindingSource;
This creates a new instance of the binding source that can be modified prior to binding it to a data source. I wrapped this code in a method for which I can pass in a string collection and the stored value (savedUnit in this case) that returns a new BindingSource instance. This is especially useful in my case, as I can now simply write:
this.cboLengthUnit.DataSource = CreateBindingSource(unitsCollection, savedLengthUnit);
this.cboWidthUnit.DataSource = CreateBindingSource(unitsCollection, savedWidthUnit);
this.cboHeightUnit.DataSource = CreateBindingSource(unitsCollection, savedHeightUnit);
I wrote the code above from memory, so it may contain a mistake.

How to force DataGridView to re-populate its rows using IQueryable<TEntity>

BlaEntities TestContext = new BlaEntities();
IQueryable<TestEntity> Entities = TestContext.TestEntity;
TestDataGridView.DataSource = Entities;
When I assign Entities to TestDataGridView's DataSource directly; I don't have to do anything to reflect my changes to the grid.
TestEntity entity = Entities.First();
entity.Title = "What up!?";
This is more than enough to see the change in the TestDataGridView. One exception I encountered was that if I add another row to the TestContext using TestContext.AddToTestEntity(...) , it doesn't show up in the grid (contrary to deleting it) but I got it working using the BindingSource's Add method.
BindingSource source = new BindingSource{DataSource = Entities};
TestDataGridView.DataSource = source;
source.Add(CreateNewTestEntity());
Now the only obstacle left in my way is this:
If I use filtering - like TestContext.Where(t => t.Active) - use it as DataSource to my grid, then change the first record's Active property to false, how do I refresh/reload the grid to reflect this without creating another instance of BlaEntities?
IQueryable<TestEntity> FilteredEntities =
TestContext.TestEntity.Where(t => t.Active);
TestDataGridView.DataSource = FilteredEntities;
TestEntity temp = FilteredEntities.First();
temp.Active = false;
I see it is not active anymore in grid but since the grid should show only the active records, how can I remove it from the grid without removing it from the source?
When I iterate over FilteredEntities, I can see that the temp isn't there anymore but I still can see and edit it in the grid. So what I need is something forces grid to iterate its DataSource (which is FilteredEntities) as well and populate itself again
I tried calling TestContext's Refresh method and BindingSource's reset methods.
I tried changing TestDataGrid.DataSource to null, then changing it back to FilteredEntities hoping to re-populate the rows, didn't work either.
It works if I save my changes using TestContext.SaveChanges() and use another instance of BlaEntities like TestDataGridView.DataSource = new BlaEntities().TestEntity.Where(t => t.Active) but I need to use my current instance.
So the question is, how can I make the TestGridView to reload its contents using FilteredEntities.
Any advice would be greatly appreciated. Thank you.
since you already have a BindingSource have a look at the ResetBindings method
//edit:
as from the comments below:
i would approach this problem with a factory for "Entities" ... if that factory would hold a ref to the last created IQueryable, it could implement the interface IQueryable itself, by forwarding all interface methods to that created object ... so it could act as a wrapper for your datasource that can replace that datasource by recreating it based on the predicate function and the actual state of all objects

How I can update a Linq-object with a DetailsView?

I use a DeatialsView with AutoGeberateColoums for my user administration.
Here my start code:
Business.UserHandling uh = new Business.UserHandling();
DAL.Benutzer user = uh.GetSingleUser(BenutzerID_Int);
List<DAL.Benutzer> listUser = new List<DAL.Benutzer>();
listUser.Add(user);
DetailsView1.DataSource = listUser;
DetailsView1.DataBind();
How can I save it automaticly without must assigning every field from the view to an object field?
Please be aware, I have different projects to cut the ASP-Layer from the Data Layer, so I must only have an "Benutzer"-Object, I can't use LinQ directly here.
(P.s.: I musst add the "Benutzer"-Object to a list, because the DetailsView won't have a single object, if someone knows it better, please correct me).
try to bind your detailsview to linqdatasource and it will automatically update linq object

Categories

Resources