This question may sound trivial but I'll ask it anyway. Given code below, what is the proper way to force ListBox to reflect new table content.
SQLDataContext dataContext = new SQLDataContext();
BindingDataSource myBindingDataSource = dataContext.MyTable1;
listBox1.DataSource = myBindingDataSource;
listBox1.DispalyMember = "Column1";
listBox.ValueMember = "Column2";
MyTable1 newRecord = new MyTable1();
newRecord.Column1 = "Some data";
newRecord.Column2 = 123;
dataContext.MyTable1.InsertOnSubmit(newRecord);
dataContext.SubmitChanges();
What is the most efficient way or best practice if you prefer, to force listBox1 into reflecting dataContext changes?
Please do not get offended but this is a classic example of misusing collections. You are using 2 parallel collections and you can't expect one to know much about one another unles they are properly linked. Since you are binding listBox1 to BindingDataSource you should perform add, remove and update operations on the same BindingDataSource. It binds both ways. Everything you add, update or delete from it will be reflected in bound controls and in the context. Just submit context changes and you will update the database. In your example, you bypass BindingDataSource and write directly to the context so `BindingDataSource' has no idea about the change.
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.
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
First let me start by explaining my use case:
Say there is a database "Cars". In that database, each row might have Make, Model, EngineType, etc. I have a page that is essentially a view of a single "Car" record, displaying its various stats. In that page, I have a user control that calculates and displys various MPG stats based on the "Car" record. The user control is passed the "Car" record via a property, then uses it internally for its calculations.
Inside that user control, something like this happens:
private void DataBindGrid()
{
gridView.DataSource = this.CarRecord.GetMPGStats();
}
Internal to "CarRecord", is a LINQ-to-SQL query, using the data context of CarRecord. For the purpose of this calculation, it would be more efficient to do this:
private void DataBindGrid()
{
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Car>(c => c.Engine);
this.CarRecord.DataContext.LoadOptions = dlo;
gridView.DataSource = this.CarRecord.GetMPGStats();
}
For the purpose of this example, ignore whether or not this may be a bad design for a user control passing in a record & datacontext.
Here are the issues I'm seeing:
The page may have set its own load options before passing the record to the user control, resulting in an inefficient query.
The page may not want the new settings specified by the user control when it continues to use the Car record itself.
So I have two questions:
What is the best way to "clear" LoadOptions? Simply set DataContext.LoadOptions = new DataLoadOptions(); or = null;?
Is there any way to set some sort of temporary LoadOptions that only affect a certain operation before reverting to whatever they were before?
Thanks in advance.
data load options always have to be set before execution of first query. once a query executes there is nothing u can do with them.
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.