Suppose you have a ViewModel with an ObservableCollection (OC) that is bound to some control inside the View.
Is there a way to temporarily disable the binding between the OC and the Control without removing the binding ? I want to have the ability to modify my ObservableCollection without having the View being informed of it.
The reason for that is simple: I am doing a lot of Add() and Insert() operations on the OC. Most of the time everything is OK but sometimes I am calling Add() so frequently that the updates in the View are looking unpleasant. For these periods of time I want to 'switch off' the binding beforehand and 'turn it on' afterwards.
Has anybody been in a similar situation / does anybody has a tip ?
Just wrap up ObservableCollection (even inheriting from it) and then override mthod OnCollectionChanged by adding custom logic which will postpone event triggering considering your requirements, I believe this is a pretty standard way to implement own defferable observable collection.
When I have a lot of items in the collection, I prefer to use a List<> as the source of a CollectionViewSource. Then the view binds to the CollectionViewSource.View. CollectionViewSource has methods DeferRefresh() and Refresh() that let you do all the background work of changing the source list without view notifications. The performance of CollectionViewSource is way better than an ObservableCollection, plus it supports filtering. Filtering might be what you need if you are constantly adding and removing items.
Related
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.
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.
I'm trying to load a rather big list of items into an WPF DataGrid. Problem is: It's painfully slow. Right now I have about 20,000 items in my list and it takes forever (well ... in my latest version it takes about 10 seconds, but that's not nearly good enough). Im working on it for the last couple of days, but I don't find a solution that is really working.
1) Of course UI virtualization is enabled (that is not the problem anymore)
2) I also tried some solution as described by Bea Stollnitz here and others. These solutions are great, but do not work for me since my collection has to be updated, filtered and sorted at runtime without reloading the collection. The solutions I found are only working with IList implementations ... I need a Observable collection to keep my items up to date.
This is the situation: I do have a Observable collection of my domain data obejcts (it's updated via WCF asychrously). I do have a ViewModel for the list items wrapping the domain objects. When I open the view I have a second list that will get populated with an ViewModel instance for each domain object. An THAT is the real problem. Since the "target" collection is bound to my DataGrid, I have to dispatch the creation of the ViewModels into the UI thread (otherwise .Net is not very happy about the collection change from another thread). So I am polluting the Dispatcher queue with 20,000 view model creation calls. Creating a ViewModel is pretty cheap, but I still have 20,000 calls in the dispatcher queue and at the same time the DataGrid is demanding CPU in the same thread to fill itself.
My idea (not quite finished): Since I already have UI virtualization in place, I would like to create the ViewModels NOT when I open my view, but on the fly then I need them. When the user can see the first 20 entries initially I only need to create 20 ViewModels and not 20,000. And here is my problem: I have no idea how. That's where you come in :)
I would like to have something like this (not working that way .. just to show what I mean):
<DataGrid ItemsSource={Binding MyDomainOjectCollection}>
<DataGrid.RowStyle>
<Style>
<Setter Property="DataContext"
Value="{Binding DataContext,
Converter={StaticResource MyViewModelFactoryConverter}}">
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
It does not have to be a Converter, it can also be something else or be done in code behind. I don't care. All I need is: Bind directly to the DomainObject collection, create the corresponding ViewModels on the fly and use the just created ViewModel instead of the original object to fill a single row. Any ideas?
Flooding your ObservableCollection and the UI getting updated for every item is the issue, I think.
You should use BindingList. You can disable events on it while you add a range of items, like this:
BindingList.RaiseListChangedEvents = false;
which will stop the UI from receiving any events. Then after the bulk add, you can enable it with:
BindingList.RaiseListChangedEvents = true;
BindingList.ResetBindings();
When I open the view I have a second list that will get populated with an ViewModel instance for each domain object.
Good, that should be your filtered ObservableCollection.
Since the "target" collection is bound to my DataGrid, I have to dispatch the creation of the ViewModels into the UI thread
NO YOU DON'T. The ViewModel is a non-UI element, it can be created on any thread.It is the addition of these items to the ObservableCollection that needs to be done on the UI thread. So you can create all the VM wrappers anywhere you like, i suggest you do it in batches and then populate the batch into the ObservableCollection as one operation. Now i know that the ObservableCollection is setup to only add a single item at a time, but that is easily remedied by adding an extension method to it, as illustrated in this previous SO answer. Note that you only have to invoke the extension method on the UI thread, once there you are fine to add the items one by one.
Personally i would fight like crazy to not retrieve all the data all the time, I presume you have already analyzed this requirement and cannot avoid it. But do you really need to do the filtering and sorting on the client? Can this not be achieved by sending data to the server which then replies with the appropriate data?
In any case, if you must get all the data, then i suggest you do it in batches (while 20K items is not a huge amount, it is still reasonable). This can be done quite easily, and using property notifications in your viewmodel you can keep your filtered collection up to date with what has been added to your source collection.
I was looking around for something like ObservableCollection<T> that would also raise an event whenever the properties of item in the collection change. I came across BindingList<T>, but was disappointed to learn that it's not available in Silverlight.
I'm aware that I could build this fairly easily by subclassing ObservableCollection<T> and subscribing to the PropertyChanged events of items as they are added (and unsubscribing as they are removed, of course). Before I do that, though, I want to make sure there isn't something I can use out of the box.
The WPF way to do this is to have the objects that you're putting in the ObservableCollection inherit from DependencyObject and have DependencyProperties instead of old fashioned properties and PropertyChanged events. This may mean you have to create a wrapper object that implements this stuff. This will make bindings from UI to the objects in your collection automatically update.
If, on the other hand, you're not doing databinding, and instead you just want a way to get an event to fire in code whenever any property on one of your objects changes, you'd probably need to create your own collection to do this.
http://msdn.microsoft.com/en-us/library/ms752914.aspx has a good explanation of DependencyProperties and examples of the implementation.
How is data binding in C# WinForms supposed to work when you have a Save button? I don't want the data updated until I press Save!
I have two forms (list and detail) backed by a BindingList<T> collection and my custom object from that collection, respectively. I can bind each form to the list or object appropriately. However, any changes made in the detail form are immediately reflected in the list form - I don't want to save the changes and update the details shown in the list until the Save button is pressed.
Is data binding designed to support this? Is there a common pattern for doing so?
Whichever way I look at it, binding doesn't seem to be able to support this scenario. I've considered the following:
Pass a clone of the object to the detail form, but then I have to reconcile the changes on Save - changes may have been made to the copy in the list in the meantime.
Implementing IEditableObject and calling EndEdit on save almost works as I can prevent the list being notified of the changes made until Save is pressed, but if something else causes a refresh the list is updated with the interim data.
I'm currently left with dispensing with data binding in my detail view, and doing it all manually. Which is rather annoying.
Data binding really wasn't designed for this kind of thing, unfortunately.
The best solution we've found here is to edit object clones, then reconcile the changes, as you mentioned. You may want to come up with a custom collection and/or object interface that helps you manage this type of thing.
Even if the cloning method is slightly more work up front, it's going to be wayyyy less complicated and frustrating than using IEditableObject trying to catch all the possible events that update the data. Not only that, it's a more straightforward approach, and you won't end up with spaghetti code.
If you are set on using a binding list, your best bet would be to implement IBindingList to create the functionality that you desire. It may also be possible to pull this off by simply inheriting from BindingList and overriding the appropriate methods to change the binding list's behavior.
http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.aspx
If you are not set on using a binding list, it is probably best to do the data manipulations manually based off of the control's events.
Best of luck.