What are the potential problems caused by an ObservableCollection supporting operations like AddRange or RemoveRange? There must be a reason why Microsoft didn't provide them, now that ObservableCollection is so frequently used with WPF.
You could implement your own collection that supports bulk operations and implements INotifyCollectionChanged. What happens if I bind such a control to an ItemsControl?
Does anyone know of ItemsControls that don't support bulk changes?
I don't think it's that there are any potential downsides or problems, it's just that it isn't there. In fact, you will find that most types in 'System.Collections.Generic' don't supply 'AddRange' functionality either.
Meanwhile, lots of people have created their own versions of 'ObservableCollection' to provide the functionality that you want. INotifyCollectionChanged contains enough information for its handlers to make note of when a range of items have been affected probably for this reason.
Last but not least, if you bind a collection that has these 'Range' type operations you will find that they will work with your UI as you expect
There are numerous extensions to ObservableCollection that can be found on the internet that add the concept of add / remove range, or allow you to defer updates and fire them manually. For example see this Stack Overflow question:
ObservableCollection Doesn't support AddRange method, so I get notified for each item added, besides what about INotifyCollectionChanging?
You can also implement a bulk add that fires a reset event, which will cause the UI to re-render all of the items in the collection:
http://peteohanlon.wordpress.com/2008/10/22/bulk-loading-in-observablecollection/
These allow you to more efficiently manage the UI updates. How an ItemsControl handles a collection changed event which details a list of changed items is up to the WPF framework itself. I am assuming it handles this intelligently!
My advice to you is, if performance is critical to you and you have collections with numerous items being updated and are experiencing performance problem, then subclass ObservableCollection to manage collection changed notification in a manner that best fits your application's needs.
NotifyCollectionChangedEventArgs includes index information. Removing items causes indexes to reshuffle, as does inserting items. Hence, whilst not entirely impossible, it would be rather difficult and probably inefficient to provide the ability to work with ranges.
There must be a reason why Microsoft didn't provide them
They do not provide every possible piece of functionality, it's (also) a cost vs. demand thing.
You could implement your own collection that supports bulk operations and implements INotifyCollectionChanged.
Yes. And when you do, you'll find that the collection will have to make choices about how/when to propagate those changes. I never tried but I imagine there are some trade-offs that the View or ViewModel can make better decisions about than a reusable collection.
Related
I'm trying to be as strict as possible when designing MVVM applications in terms of separating layers. In particular, I do my best to never leak anything platform-related to my viewmodels, and by platform I mean OS-related stuff, but also WPF-related stuff. Even though I barely ever reuse my viewmodels for different platforms, I try to keep them reusable as a good practice.
Some time ago I stumbled upon problem of sorting, grouping and filtering collections. The obvious solution for that in WPF is CollectionViewSource. But then things start to get complicated.
First, the facts:
ObservableCollection lies in System.ComponentModel, so we may treat it as non-platform (safe to use inside viewmodel).
CollectionViewSource resides in PresentationFramework assembly, so i treat this class as WPF-native, so platform and thus unusable in viewmodels.
CollectionView also resides in PresentationFramework, so I cannot use it in viewmodel either
ICollectionView (which obviously CollectionView implements) in turn resides in System.ComponentModel (WindowsBase), so it seems to be non-platform.
I always struggled, how to properly implement the whole CollectionViewSource-thing. Usually I:
Place CollectionViewSource in window/control resources and bind its Source to viewmodel's property
Bind list control's ItemsSource property to the CollectionViewSource by the kind-of weird binding: ItemsSource="{Binding Source={StaticResource cvsSource}}"
Implement filtering and sorting in the window/control, only consulting viewmodel when needed (e.g. getting filter string from it)
This seems kind of sketchy to me though.
The problem is generally, is sorting, filtering and grouping considered part of presentation or business logic?
Theoretically, presentation. But then one may quickly realize, that even though filtering a collection is related to presentation, knowing how to filter it boils down to business logic (-> viewmodel).
Since though ICollectionView seems to be platform-free, I consider the following scenario:
Place CollectionViewSource in resources as earlier
Bind ItemsSource the same way as earlier, but then
Extract ICollectionView from CollectionViewSource in the view (window/control) and pass it into the viewmodel. This will give viewmodel full control over sorting, filtering, selecting etc.
Is it a proper way of using CollectionViewSource? Or am I still getting things wrong and it should be solved yet in another way? Is there any documentation which shows, how to properly use CollectionViewSource according to MVVM requirements? (in terms: not instantiating it inside viewmodel)?
The problem is generally, is sorting, filtering and grouping considered part of presentation or business logic?
The purpose of layering is better maintainability through minimal interfaces. So, imho, "just give me the data, I'll present it" is the minimal interface. But reality is harsh, and so you might need to e.g. to filter your data in backend due to performance reasons. But this makes your interface more complex, so it's a compromise.
I would almost always put grouping and sorting into the presentation category, because you can easily imagine very different views tailored to different user roles with different requirements about sorting and grouping.
There are several reasons why I would not usually use collectionview or collectionviewsource for filtering and sorting.
Two outweigh most of them though:
Collectionview filtering and sorting is inefficient.
That might not matter, depends on your data.
If you have just a few hundred rows you're possibly going to deal with then it doesn't matter if it's slow.
There is often too much data.
If you have thousands of transactions a second you're obviously not going to pull that table across onto the client and then start thinking about filtering it. It's not going to even get there.
You don't need very many rows before it becomes advisable to do that filtering on the server. When you have substantial amounts of data ( and most systems I have worked on do ) you need to think about paging as well. That means you need to sort on the server as well.
I also do not like sorting on the UI so much.
It's expensive to test UI based logic. I prefer minimal code in the UI. This BTW is the reason to encapsulate any UI code you have for re-use in behaviours. This is why you don't want much code in your code behind.
I would not be too concerned about viewmodel libraries necessitating "view" dll.
With two caveats:
Unless
a) You're developing with MAUI or some such re-usability in mind
OR
b) Your automated test vm is linux.
Otherwise it's academic what windows only dll are required. The wpf app will definitely require those when delivered.
I've frequently used WPF to bind a datagrid to a collection of IEditableObject's. Almost every time I do this, it becomes something that I soon regret. Is there an unspoken rule to avoid implementing IEditableObject in my viewmodels altogether?
The most recent problem I've noticed is a memory leak. Some critical internal components (DataBindEngine, CommitManager, BindingGroup) are coordinating together to root memory references. It seems to be happening if the user closes out of a data entry window at an unexpected moment (eg. after a BeginEdit if the user never did anything to trigger a call to "CancelEdit" or "CommitEdit"). The rooting happens in a place that is quite deeply hidden within the databinding engine and it seems extremely difficult to control the default behavior at that level (aside from ensuring that I don't implement IEditableObject in any of my viewmodels).
Unfortunately it isn't always easy to avoid IEditableObject implementations. For example, when binding to ADO.Net, the datagrid wants to use a BindingListCollectionView (for the collection) and the DataRowView class for the individual records. Since the DataRowView class implements IEditableObject, there does not seem to be any way to easily forego the related bugs. I wish there was a way to tell the WPF datagrid to ignore implementations of IEditableObject, and pretend that it wasn't implemented.
On a general basis I'd like to know whether we should avoid binding to IEditableObject (and by that rule we would also avoid binding directly to ADO.Net as well). There seem to be a number of pitfalls to doing so, and I've heard WPF developers who are disparaging of ADO.Net. But I've never seen any formal or authoritative guidance from Microsoft on this matter.
PS. Aside from memory leaks in my databindings, here are some other examples of the types of troubles you can run into when binding to IEditableObject.
Unexpected problems with late binding updates
https://social.msdn.microsoft.com/Forums/vstudio/en-US/d48ccbca-6a3c-4aea-9875-4862fc4a49b9/wpf-datagrid-validate-cell-when-leave?forum=wpf
IEditableObject conflicts with BindingGroup (both attempt to serve
similar purposes)
https://social.msdn.microsoft.com/Forums/vstudio/en-US/7f7196b8-b9dc-487d-93cd-e77f5b3d9906/confused-about-transactional-editing-edititemcanceledit?forum=wpf
IEditableObject protocol problems (because of overlapping goals
with the BindingGroup class)
WPF DataGrid calls BeginEdit on an IEditableObject two times?
Potential fixes (uservoice idea):
Ideally the WPF datagrid would have a property that would allow us to disable all the specialized handling of IEditableObject. This would allow the datagrid to work in a consistent way, whether a viewmodel implemented that interface or not. I found this user voice idea about making the BindingGroup functionality "optional" in a datagrid. I suppose this could have been part of the solution, but the idea was rejected.
https://wpdev.uservoice.com/forums/427690-windows-presentation-foundation-wpf/suggestions/6736492-make-internal-use-of-bindinggroup-in-datagrid-opti
I have a cross-platform project written using MVVM-pattern (no specific frameworks used, just self-written implementation). Project has several independent modules each of which has several pages.
Each of pages has ViewModel and some sort of manager who's responsible for data-oriented logic (get, save, delete, transform etc.). So the data-flow looks about this:
VM -> Manager -> Service -> Manager -> VM
When VM is loaded it's asking manager for data. Manager perform service call, get data, construct collection of models from DTOs, return this collection to ViewModel which converts collection of models to collection of ViewModels
to be rendered in the list.
Now I'm looking for a way to implement this kind of logic using Rx. Most of pages have one main list to be edited (items inserted, deleted, modified) and several support collections (providers for some combo-box to choose value from).
Support collections can be easily retrieved via standard async/await calls or via converting tasks to Rx - they are no problem. But the modifiable list is.
I just cannot figure out the way to track changes in this list for the entire life of the page without breaking Rx logic.
I have options to subscribe to:
IEnumerable<Model>
Task<IEnumerable<Model>>
IObservable<IEnumerable<Model>>
but I suppose I have to subscribe for IObservable<Model> because I need a way to track individual changes.
And I need a way to modify this collection from other methods like Add, Delete or Edit.
So should I create IObservable via Observable.Create (or other method) and store IObserver somewhere inside Manager to call OnNext or OnError in other methods? But it doesn't looks like an Rx-way-to-do.
Do you have any suggestions about my problem? Any advise appreciated. Thanks.
PS: you may say Rx isn't the best way to solve my problem with tracking modifiable list because it's not endless stream of events and I have to push modifications by myself but Rx has very convenient way of filtering data and handling errors so I'm really looking forward to implement it in the application.
Look at ReactiveList<T> - it's part of ReactiveUI framework which we've used to great effect with MVVM / WPF UIs.
ReactiveList works, although it doesn't have any single Rx stream exposed which provides all modification events.
See this related question, which answer is to use an IObservable<IObservable<Model>>, with each inner observable representing one item from your list, along with its modifications (and deletion - when it completes).
I've been learning WPF for a few months now and I'm curious about one thing. How does a Binding actually work? I mean, what happends, under the hood. I don't expect that anyone here would give a detailed explanation but maybe a good resource or link where to read up on something like this. I've been searching and googling for this but no good hits so far.
I realize that to fully understand this you would probably have to understand most parts of the framework but a little basic understanding would be great.
Thanks
There are two aspects to consider in binding, getting values in to the UI and having the UI be notified of changes in its DataContext.
Basically you can bind almost anything to any POCO object, the object does not need to implement anything special. The restriction with plain objects is the binding target will not be told when the underlying value changes.
Property changes are propogated through one of three mechanisims:
Dependancy Properties : will notify the binding system when its value changes, can also be used for animations.
INotifyPropertyChanged : If the binding is to a property on an object which implements INotifyPropertyChanged, the binding system will look for subscribe to the PropertyChanged event and update the binding target, when this event is raised, property names are sent as Strings.
*Property*Changed events : The last thing bindings will look for is an event with a name the same as the source property and Changed on the end, so a Name property would need to have a public event called NameChanged, this allows WPF to bind to older .net classes such as 1.1.
I don't know much about the specifics of binding in WPF, but if it's the same principle behind binding in System.ComponentModel and Windows Forms, then a blog article I wrote recently might help to shed some light on it for you:
Some Background on Windows Forms Data Binding
Basically, binding sources have to implement IList, IListSource, ITypedList or IBindingList. Through either reflection or explicit definition, data sources expose PropertyDescriptor objects which describe their bindable properties. The names of these properties (which may or may not be the names of actual properties on the objects contained in the data source - e.g. in DataTable, they are column names instead) are matched against the DisplayMember/ValueMember property or, in the case of WPF, the binding path.
This is quite a difficult question to answer. I believe there are roughly two aspects to an answer. The first is the documentation. If you go through all the documentation of binding expressions, including how paths are build and e.g. the helper classes like BindingOperations, you can find out a lot. If that's not enough, you can always dive into the code by downloading it from the Microsoft Source Initiative site.
I'm still new with C#, and I'm working on a project where we also use WPF, and the WPF DataGrid Toolkit (See at CodePlex), which hasn't yet been released into the framework. Due to the nature of the project, it's 100% sure we will be altering some controls of the assembly later.
My co-workers have decided to redefine every control from the datagrid within a namespace of our project, and ihnerit the specific control of the assembly namespace.
So instead of using:
clr-namespace:Microsoft.Windows.Controls.Primitives;assembly=WPFToolkit
clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit
We'll be using our own xxx.Controls and xxx.Controls.Primitives Namespaces.
This way, it would be pretty easy to alter the ihnerited controls.
Somehow, I got a bad feeling about this solution, but I'm still inexperienced and cannot tell if such an approach is legitimate or not, or if there is another good solution to our requirements (altering the controls later without changing too much code in multiple files).
It would be nice if you express your opinion to this approach.
What kind of alterations are you talking about? It seems like a waste of time to pre-emptively derive from each of the classes before you know what you'll actually need to change.
When you do need to change something, it shouldn't be too hard to create the derived class at that point, and fix up any references - which may only be for some instances rather than all of them. Yes, it may mean check-ins involving quite a few files - but if you're using a sensible source control system it will be an atomic change, and it will be obvious why it's changing.
With your current approach, there's no immediate "these are the controls we've had to change" - if you do it in a "just-in-time" manner, you'll be able to tell just by looking at what derived controls you've actually had to create.
I agree with you. The alterations, or better said, the changes, can be of any kind. Behavior and etc. And the changes should be make just in time.
Unfortunately, that is not my decision. Some stubborns are at work :)
But what is interesting me is if a complete different approach to the whole idea exists?
Say, I've got a DataGrid, the project evolves, and now, I've got to do some drastic changes in the validation behavior of dataGrid rows.
This could also apply to a lot of controls.
The problem with our project is, we have a kind of complex data access layer, which not only provides data, but also actually controls it. This means data isn't read,modified, deleted or appended without including some logic provided by the data access layer.
For an example, the datagrid doesn't directly delete rows, but instead, we overwrite the delete behaviour and aks the data access layer to delete it. With binding, this works pretty good for now.
This kind of scenario will apply to a lot of other things in the future, regarding CRUD operations, validation and etc.