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.
Related
I am developing a C# MVVM WPF app and have problems to decide whether I should use the message mediator pattern or simple INotifyPropertyChanged for my UI 'live' Model change notifications. The problem in particular is that my model represents a graph with lots of 'live' objects that all have properties where different viewmodels will be interested in the changes at some point. I have about 3-5 viewmodels active that need notification of model changes. Some changes might be nested deep inside the models 'grandchildren'.
I tried to compare both messaging techniques, mediator pattern and INotifyPropertyChanged, and think that the mediator is better suited for change notifications between different modules/systems. My viewmodels definitely need the initial values of the model upon initialization and then change notifications afterwards. INotifyPropertyChanged seems to be the optimal choice in my case, but I am a bit skeptic, because I think lots of nameof(e.PropertyName) switch cases are not very elegant. Are there better alternatives for this problem?
As is typical for such cases, you could have both ways. You will obviously not be able to avoid using INotifyPropertyChanged because this is the WPF way of handling MVVM-based Data Binding, so all your view-models will be at least basic implementations of this interface.
I suspect that you might simply need both. I would not suggest using the INotifyPropertyChanged-based pattern inside your model. The reason this interface was created was to base the observer pattern on actual property names as strings. This was pretty much a "one-way street", as XAML, the typical "design" language supporting WPF, is parsed in a "stringly" manner, leaving few efficient alternatives. In my humble opinion, there is almost no reason that you would want to force notifications throughout your model to be as weakly-typed (stringly-typed, in fact) as this scenario, so I would suggest avoiding INotifyPropertyChanged if you have a relatively voluminous Domain Model.
Messaging would probably be the way to go, maintaining strong-typing with message type registrations and whatnot, unless you would like to consider other alternatives, such as simple C# events, or even combine both. While at it, try to stick and adhere to hierarchy and encapsulation as strongly as possible but also as weakly as practical*.
(*I mean, if some of your Domain objects reach multiple nesting levels, then, occasionally, it may be more practical to bypass hierarchy and let, for example, grand-grandchildren send messages of their own, instead of notifying their parents' parents through events, and letting these parents' parents then send the corresponding notification messages. In typical OOP programming, compromises are constantly weighted against practicality and time availability).
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'm working on a WPF project using the MVVM pattern and I was wondering if I can improve the internal structure of my ViewModel by abstracting its exposed properties into separate classes.
It's common practice for VMs to contain lots of properties laying around in the same class: Some are used to retrieved user inputs, some are used to expose commands, others to expose the models, and probably some other properties for the view model's own business logic. Not to mention that these properties often have set and get bodies that adds some bulk to the package. This can quickly become messy inside a VM class and finding one's way there can become challenging.
As a way to solve this issue, I am exploring with my team the idea of grouping properties inside my VM into different categories. As a first approach, I chose to group them this way:
ViewData, UserInputs and Commands, each one represented by its own class. Then I referenced them as properties inside my VM class.
My intention is that these classes will only act as placeholders to free up the bloat in my VM and keep it clean and focused only on interaction logic for handling user events.
It's a simple refactoring, but I get the following pros:
Cleaner and readable VM.
Easier binding from the XAML because you know what the entry point is/should be.
Let me elaborate on the latter one: If I want to bind a text box to a property of my VM, I know the binding expression should start with Userinput.MyVMProperty. If I need to show a value from my VM, I know my binding's entry point is going to be ViewData.MyOtherVMProperty. Binding intellisense will also become better because when you know your entry point, the
suggestion list would be smaller and more focused. This also works the other way around: when reading through your XAML controls, any binding that starts with UserInput necessarily means it's a a control that should send data back to the VM.
The only cons I can find is that this will require creating extra classes for each VM, but I believe it's a fair price to pay for the benefits you get.
Take note that the grouping I suggested may not be the best, but I don't mind any other grouping as long as it solves the problem with bulky VMs.
So, has any one ever tried a similar pattern? Do you think this is a good idea/practice? What other good practices I can use to improve my VMs?
Bonus question: One developer in my team who seemed to agree with this idea, suggested to go the extra mile and consider the grouped classes as VM dependencies and that they need to be injected inside the VM. What do you think about this?
So for every ViewModel you need to create own inner classes for every group. You cannot use Interfaces because ViewModels have different properties and commands
Then did you considered that every "groupclass" must to "know" about other groups for ViewModel will work properly.
In my opinion Class should be solid logical structure with minimal outside dependency.
Based on the pros you trying to achieve I can suggest another approach without changing structure of the ViewModel classes
Readability can be achieved partly by #regions.
Or use Partial class for separating different groups in the different files,
while keeping them inside one logical structure
Intellisense can be improved by naming conveniences - using prefix based on group property belong to. For example UserInputMyName, ViewDataFullName or CommandDelete
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.
Recently i got explained that MVVM can only be done "the right way" if i use DataTemplates. Is this truely the case?
I'd say its a good idea to use DataTemplates if you want highly reusable "Views".
But if i am going to develop an application that has, say, five to ten different pages, and there is very little to none reuse of specific controls (like the "Person" view is only used once, and its highly likely that this requirement doenst change), why cant i simply create a usercontrol and put that directly into the code?
Am i missing some important core principle of MVVM here?
Main selling point of MVVM is separation of View from the ViewModel (so that VM doesnt know about View) by using powerful Binding feature of WPF.
DataTemplates are a just another feature which allows you to represent data in different way. As you have said, if you dont have reusable DataTemplate then dont create one, even if you do make sure it resides in the View's Resources, you can share it wider group if you wanted do.
using UserControl can be useful where you need to do something extra (apart from simple representing data), for example, some complex validation or extra commands/buttons
I dont think MVVM and DataTemplates are related in the same context.
There is no special needing for DataTemplate, you have a view and a viewmodel that cooperates with databindings and events. The MVVM goal in WPF is to remove the code from the view to achieve a real presentation only view, and not a messy code behind store. Having the ViewModel agnostic from the view is another goal, even if not always achieved.