ComboBox doesn't automatically update when DataSource changes? - c#

For some reason when adding or remove items from the DataSource (a simple BindingList) the ComboBox updates accordingly but if I edit an item like this, it doesn't update automatically:
myBindingList[index].Name = "NewName";
myBindingList[index].Value = newValue;
In order to get it to update when I edit an item as opposed to creating or removing an item I have to do this after the change is made:
myComboBox.DataSource = null;
myComboBox.DataSource = myBindingList;
This fixes the problem but it seems like a rather messy solution. Also with large lists it may become slow (premature optimization I know) but still is there a way to force the ComboBox to update without completely re-assigning its DataSource?
Thanks for reading.

this is stated in the MSDN forums:
The IBindingList interface contains the ListChanged event where
controls like the combobox hook up into if the underlying datasource
assigned to it implements the said interface. your datasource must
raise the corresponding ListChanged with proper ListChangeEventArgs if
ever you add, remove, change, etc. your IBindingList implementor.
this way, whenever the underlying source you used to bind to your
combobox is changed, the corresponding UI control (combobox) is
refreshed.
you say you are using BindingList and in fact you do get the combobox to reflect add or remove items events. I think you should do the update of the items already inside your BindingList in another way because looks like the proper BindingList events are not firing.
you could either investigate into that or simply live with reset and reassign the DataSource, I don't think is too bad, you are in Statefull Windows Forms application not in SatetLess Webforms so you do have your objects there all the time :)

You need observable collections and IPropertyChange implementation:
ComboBox bound to a ObservableCollection does not update

Related

Get notified of collection changes

My class has a public List field. I want to detect when someone changes this collection. However, writing my own Add/Remove wrapper around this collection seems wasteful. Is there a way to get notifications about changes in collection with delegates or something like that?
Use ObservableCollection<T> instead
you may find reference here.
There already exists a collection you've described and it's called ObservableCollection.
It has CollectionChanged event, so just subscribe your event handler there and i'll get called every time item is added or removed from collection..
Use BindingList<T> instead. It offers a ListChanged event. It is also accepted as DataSource in many controls such as a Listbox or a Grid.
As mentioned in this answer the BindingList offers a lot more than ObservableCollection
If you want to change a list in a listbox and changes to that list be reflected to the UI, you can set a BindingList<> as datasource to the Listbox and then simply manipulate the list. The Listbox will handle the ListChanged event and will display changes without having to manually do it by your self.
If you are going to use it in Winforms you should go with BindingList and if you are going to use it on a WPF app then go with ObservableCollection.

is this the way BindingList should work?

I have a DevExpress GridControl bound to a BindingList. I used a BindingList so that changes are automatically applied to the bound control.
It works perfectly when I add or remove items from the list, but when I change an item, it doesn't quit work the way I want it.
I created a foreach loop that runs in another thread (to keep the UI thread free to update) that iterates thru all the objects in the BindingList and changes some of their properties.
The way I expect it to work is that each property updated updates on the GridControl in real time. However the updating is very "chunky". Sometimes it updates 2 rows at a time, sometimes I have to click the GridControl to get the new values.
Why does this happen?
Is it a good solution to call DataControl.RefreshDataSource() after each item?
but when I change an item, it doesn't quit work the way I want it.
Becasue changes within an item are not somethign the BindingList cares about -that is why the items should implement INotifyPropertyChanged.
Basically the binding list says when the LIST has changed, but if you update a property - how should the list know ;) And why should it care - every item is separately responsible for publishing updates to its properties using said interface.
Is it a good solution to call DataControl.RefreshDataSource() after each item?
Worst way. Make sure the items implement INotifyPropertyChange and raise the PropertyChanged event accordingly.
The objects in your BindingList should implement the INotifyPropertyChanged interface and raise the PropertyChanged event when the value of their properties change.

INotifyCollectionChanged -- How often does it fire (and how do they make it so efficient/fast)?

Basically, I'm wondering how it is actually efficient here.
Sample code:
void GetItems()
{
foreach (var item in items)
myObservableCollection.Add(item);
}
Won't this fire off the CollectionChanged event every time causing the UI to have to refresh everytime? Or does it do it so that it waits til the GetItems function is done?
Basically, it seems that WPF handles it very well, and I'm wondering how they did that.
Optimizing Performance: Data Binding provides some background on how data bindings are resolved, including the performance implications of different items sources. Take a look at the Binding to an ItemsSource section.
Consider a scenario in which you have a CLR List object that holds
a list of employees that you want to display in a ListBox. To create a
correspondence between these two objects, you would bind your employee
list to the ItemsSource property of the ListBox. However, suppose you
have a new employee joining your group. You might think that in order
to insert this new person into your bound ListBox values, you would
simply add this person to your employee list and expect this change to
be recognized by the data binding engine automatically.
That assumption would prove false; in actuality, the change will not
be reflected in the ListBox automatically. This is because the CLR
List object does not automatically raise a collection changed
event. In order to get the ListBox to pick up the changes, you would
have to recreate your list of employees and re-attach it to the
ItemsSource property of the ListBox. While this solution works, it
introduces a huge performance impact. Each time you reassign the
ItemsSource of ListBox to a new object, the ListBox first throws away
its previous items and regenerates its entire list. The performance
impact is magnified if your ListBox maps to a complex DataTemplate.
A very efficient solution to this problem is to make your employee
list an ObservableCollection. An ObservableCollection object
raises a change notification which the data binding engine can
receive. The event adds or removes an item from an ItemsControl
without the need to regenerate the entire list.
Update time for 1 item (ms)
To a CLR List object = 1656 ms
To an ObservableCollection = 20 ms
WPF never binds directly to a collection. If you specify a collection as a binding source, WPF actually binds to the collection's default view.
A collection view is a layer on top of a binding source collection
that allows you to navigate and display the source collection based on
sort, filter, and group queries, without having to change the
underlying source collection itself. A collection view also maintains
a pointer to the current item in the collection. If the source
collection implements the INotifyCollectionChanged interface, the
changes raised by the CollectionChanged event are propagated to the
views.
The event will fire for every change.
The GUI does not have to react and refresh every time, it can postpone that.
I know WinForms will optimize this, I think WPF has a similar approach.
If you want to see how often the UI requests the fresh results expose it as a public property and put a debug line in the get (assessor) of the public property for myObservableCollection.

Bound ListBox SelectedIndex keeps changing

I have a ListBox that is bound to a List. Every-time the ListBox updates to reflect the collection, the SelectedIndex changes to the top item. How can stop this behavior and retain the current SelectedIndex?
[UPDATE]
I found a better collection to use for this kind of functionality - the 'BindingList': http://msdn.microsoft.com/en-us/library/ms132679(v=VS.90).aspx.
WulfgarPro.
When [...] updates to reflect the collection
Does that mean there is a new collection? If so what does "the same position" mean?
When re-binding to a(nother) list, you will have to save & restore the index position. Just write code around the place where you update the DataSource.
Using the BindingList collection rather than a List remedied a lot of my problems. I was originally using a Thread and a Delegate to query the collection and call ListBox.DataSource=[..] to update the binding. This was slow, cumbersome and error prone. Not to mention my original problem of not being able to easily retain the SelectedItem. Changing to to the BindingList allowed me to remove the thread and delegate and now everything works as intended. Assigning the desired display property for the ListBox can be achieved using ListBox.DisplayMember=[..].
WulfgarPro.

Databinding a List of custom objects to ComboBox collection

I'm working on a project that controls multiple devices over USB and intercepts the WM_DEVICECHANGE events from Windows to manage currently connected devices and plugin modules using those devices. There exists a combobox on the main form that (should) display all available devices (the custom object) and be dynamically updated in the event of either device arrival or removal.
Now, every tutorial that I have read including some from here have directed me to set the DataSource property of the combobox to the List of objects that I have, and leave the DisplayMember/ValueMember property blank to display ToString() and to return the object as the value.
In some instances I have tried (such as post instantiating the List and populating it with sample/real objects) the combobox populates, however upon removal or arrival, the collection in the combobox does not update even when reassigning the List to the combobox's DataSource property after every device arrival/removal method.
EDIT: Oh right... the question...
How do I databind the collection of (custom objects returned as the values) dynamically such that it can handle sudden changes (additions/removals) to the list's contents?
EDIT 2: Sorry I didn't make this more clear, wasn't aware of how pervasive WPF has gotten, but this is a WinForms project.
Thanks in advance for any help you can provide. :)
even when reassigning the List to the
combobox's DataSource property after
every device arrival/removal method.
After reassigning, you need to call .DataBind() again.
If you're using .net 3.5 or above, you can use an ObservableCollection to have the list update automagically.
I'm not sure if you're using WinForms or WPF. I'm assuming WPF as nobody should start out on WinForms nowadays...
You need to change your List to an ObservableCollection<T>.
The WPF binding model relies heavily on a magic subsystem of fairies and warlocks (not mocking you, I'm actually serious). They have their own language for informing each other that something has changed between the DataContext and UI. These are primarily the DependencyObject/Dependency properties, INotifyPropertyChanged and INotifyCollectionChanged, and the ObservableCollection<T>.
By proper use of these totems, changes in your ViewModels/Models (the stuff you stick in the DataContext and/or bind to the DataSource) will be reflected on the UI.
I don't know about that ToString() bit. I always set the DisplayMember to the Property I want to display. I don't care about the ValueMember because the SelectedItem is my Object.
DataBind doesn't apply if this is a WinForms project, which it sounds like. (USB and stuff)
I don't do anything funky in my project when up update the data in a ComboBox, I just set the DataSource again.
EDIT:
You could try using a BindingList rather than a List also.
http://msdn.microsoft.com/en-us/library/ms132679.aspx

Categories