Say I have a main database class, TvShowDatabase, which contains a list of Shows. Each Show has a list of Episodes, each of which has a Length. The objects TvShowDatabase, Show and Episode all implement INotifyPropertyChanged.
I would like to compute and show TotalViewingPotential, a property of TvShowDatabase, which sums the Length of each Episode of each Show.
Moreover, let's say the user is looking at this structure in a tree style, and is able to edit the Length of any Show. When they do this, the TotalViewingPotential should update accordingly, and the results be seen on the screen.
My question: in WPF (and specifically, using Prism for MVVM), what is the best way to hook up the plumbing for these change notifications?
I've considered intercepting adds to each list (using ObservableCollection) and giving any new items a Parent. This, however, gets messy, and a top-down approach is preferable.
I've seen hyper-observable collections (http://www.codeproject.com/Tips/694370/How-to-Listen-to-Property-Chang), but I worry about event-subscription based memory leaks with this method, and it still requires a good bit of manual plumbing to raise the OnPropertyChanged(TotalViewingCollection) events where needed.
Is there a pattern for this?
The task, as you describe it, has a simple solution. You do not need to propagate changes along the collections.
TotalViewingPotential is a normal INotifyPropertyChanged property. Make it accessible from your Length properties.
In the setter of the Length property, compute the difference between the new and the old values, and increment the TotalViewingPotential by the difference.
That's all.
To speed up initial loading, in order not to generate notifications for all Lengths, you may set the values of the backing fields of Lengths, and compute and and set TotalViewingPotential based on the values from the DB.
Related
I have a form that facilitates a long running operation that produces a running log. What's a good way to implement this with MVVM / WPF?
I could just bind a string property to a TextBlock or a TextBox and keep updating it, but that seems like a lot of string generation. Or, I could have a list of strings, keep adding new strings to it and bind to a list box.
Is there a third option or is either of the above preferable to another?
I think, aside from the MVVM consideration with wanting to leverage INotifyPropertyChange, it's all a matter of style as opposed to hard and fast set of rules. Any way you go, you'll be generating new strings for each update so I wouldn't be terribly concerned about it unless memory becomes a constraint and you have to throw away older strings or maintain some sort of current buffer. You've been considering the approach I usually use.
When I've done this, I've used a TextBox, but I don't have a strong argument on preferring it over the TextBlock. I also usually use an ObservableCollection<string> rather than a list just for the MVVM INotifyPropertyChange goodness, and with each update add it to the collection.
We're rolling our own special Items-based control/panel combo that isn't based on a subclass of ItemsControl (there are multiple reasons why we can't do this which I won't get into here) but our control does handle thousands and thousands of data items, so we'd like to implement Visual virtualization.
What we're wondering is if it makes more sense to try and bang the existing ItemContainerGenerator class into our design or roll our own virtualization method.
On the one hand, ItemContainerGenerator already handles virtualization and is very efficient, and tried-and-true code is almost always preferable to starting from scratch, but on the other, the ICG was designed specificially for use with, and relies on features of the ItemsControl, which again, this control is not. Plus, that is a container generator but in our case, we just need to generate and lay out a specific, known visual that represents the data item.
Now, perhaps I'm over-simplifying things, but all I see is a need to determine which items would be visible in the ViewPort, ensure visual representations of those items are created, measured and arranged, then discard any left-over already-created visuals. To keep track of it all, it seems a simple item-to-visual mapping scheme is all that is needed, perhaps with a ItemVisual attached property on the DataItem's ViewModel object. That way when an item is removed/destroyed, we just check if there's an existing visual, and if so, nuke it.
That said, can anyone think of a reason we shouldn't simply roll our own visual virtualization? Again, the ICG does this, but I'm wondering if that's taking a Semi to the store to get eggs.
Simple example:
I have VM with hierarchical item structure and I have designed View containing TreeView. Each tree node corresponds to data item related to the one in the Model.
GUI must respect user preferences and settings and store different parameters including whether each particular node is expanded or not. As long as every node relates to some data we need to tie View settings with the Model. I beleive it's not OK to define property IsExpanded in the class standing for the Model.
So where should I store the settings so they stayed consistent with the data?
I believe it's not OK to define property IsExpaned in the class standing for the Model.
Why? Will there be occassions when the model is linked to two different tree-views?
If that is the case you can certainly create an "adapter view-model" that wraps your models and adds the property. Just be careful because that can technique can easily lead to a memory leak.
Otherwise, it is perfectly acceptable to put it right in the model, especially if that model is being used specifically to serve that view. As with all things, start with the simplest, most pragmatic approach.
I was in a similar situation, here's what I did to solve it.
Each ViewModel node had an IsExpanded property, which was bound to the TreeViewItem's IsExpanded property. I didn't want to persist the state of the tree by storing it in the Model in IsExpanded properties (it's to do with visual state right?). So instead, I had the VM tree structure generate a dictionary that stored the expansion state for each node against a string key generated from the node's state in the tree:
Dictionary<string, bool> treeExpandedStates
Each node in the tree had an ID, so in my case the key was something like "/1/3/7", but anything unique will do. This Dictionary was then serialized to a file on application close (in actual fact it was a SerializableDictionary). Then on application restart, it was deserialized and used to set the expansion state after the hierarchy had been loaded back up. This meant that the state of the tree was exactly as the user had left it, but nothing was stored in the Model.
I recognize the problem as a common dilemma in MVVM. I can look at it from two sides.
Approach A)
In the separation of View, Model, and ViewModel, what you describe resides in the Model. You write for example that it needs to be stored. That does not mean it is the same part of the model as the other model data.
Consider the following separation:
FolderModel - A model of the content or properties of a folder.
TreeNodeModel - A model of a users choices when exploring the tree view.
It might not be that easy to do a separation just like that, but the point is that MVVM is not meant to force you to stuff everything in the same place, and I do not think MVVM prevents you from keeping models for user interactions, the same way as you keep models for data content. Microsoft writes:
The data could come from a database, a Web service, a named pipe, a file on disk, or even carrier pigeons: it simply does not matter.
Why should not data for a data model be able to come from interactions of a user? :)
Approach B)
My usual approach to these dilemmas, though, is that properties like IsExpanded does mostly not need to be stored between sessions. That way, a property in the ViewModel, with a default value instead of a stored value, suffices. :)
And if it needs to be stored, it does not need to be stored in the Model. The ViewModel is for logic for presenting model data. If the ViewModel wants to save its logic state, that does not have to be stored in the Model.
Summary
In my point of view, you can store it any of these ways without breaking the MVVM pattern.
I have asked this question on MSDN forums as well ...
http://social.msdn.microsoft.com/Forums/en/wpf/thread/4493988a-9bd8-48fe-aff0-348502136a80
I need to know that why Microsoft suggests that BindingList is not properly supported in WPF...
What is it that doesnt work with BindingList in WPF? I find it pretty useful as it is. So far I personally have not found BindingList any slower or a having more load on memory.
Plus WPF ItemsControls, ItemsTemplates, Styles, Hierarchies work great with BindingLists too. They are equally observable.
Being a hardcore WPF developer myself and an ObservableCollection fan, my faith is getting shaken by a been-there-done-that BindingList....
Why should I use ObservableCollection over BindingList?
(keeping aside INotifyPropertyChanged which both have to implement for item property changes)
This may be of interest:
http://www.themissingdocs.net/wordpress/?p=465
most important paragraphs:
But the implementation does not scale, it is slow, it performs terribly with larger lists. If your element type supports INotifyPropertyChanged, every time one of those elements raises the property changed event the entire list is walked to work out the index in the list of the item which raised the event! I was in shock when I first realised this. You see BindingList is truly just a rather thin wrapper over Collection, so there is no metadata associated with each entry, all of the binding of the element PropertyChanged event is directed to a single handler, and all it gets given is the source and the name of the changed property, so there is no way to include the NewIndex parameter in ListChangedEventArgs without doing a search. (By default this search even uses the default object comparator, so if you happen to have two different but sometimes equal objects in your list, enjoy the results…)
Another side note – AddNew, the other feature which BindingList has which Collection does not – also does not scale. It has to use IndexOf to find out where in the list the newly added item ended up in case it needs to cancel the add, because it supports auto sorting in derived types. (BindingList does not support auto sorting itself…)
Advance apologies for the event-style explanation; there's a lot of factors that I feel all play a role of their own. WPF is not my native framework of choice, and it probably shows. :)
Old situation: I had a window with several controls. Depending on their selections, I used multibindings and a converter to determine whether certain controls needed to be shown that inform the user about the implications of their changes before they'd eventually confirm them by OK (or simply dismissed by using Cancel). This worked perfectly.
Problem: Too many controls as time went by, too much clutter.
Solution: Put stuff in different Pages so it becomes easier to browse for the user. In order to have changes-to-be persist as a user arbitrarily browses between the pages, I create these dynamically and put them in a cache (Dictionary<string, BasePage>, see below), from which they will be pulled as the user chooses them.
Consequence: I need to decouple the bindings to the notification controls as the different options are now on different pages.
Solution? I put a BasePage class in that exposes certain abstract read-only properties that define the various aspects that the window needs to know about in order to do its notifications. For example, a bool requiresReboot property defines whether the current state of things on that page requires a reboot to take (full) effect. A specific page implements the property based on its controls.
Problem: I do not know how to keep create a proper binding that properly gets updated as the pages are changed. I tried giving my notification controls a binding to the Dictionary<string, BasePage> with a converter that checks all pages and the relevant property.
Questions:
1) How do I create a proper property for this purpose? I presume I need a DependancyProperty as I did a fair bit of reading on MSDN, but I can't figure out how this fits together.
2) How do I make a link between my custom property so that it allows (multiple) control(s) on a page to change that property? Do I use INotifyPropertyChanged somehow? My old example bound against several CheckBox.IsChecked properties in XAML. I am trying to avoid putting tons of events (OnChange, etc) on the controls as the original code did not need it and I have been told it makes for a messy solution for as far WPF is concerned.
3) Finally, I suspect I may need to change my Dictionary<string, BasePage> class to a custom implementation that implements some sort of INotifyPropertyChanged but for Collections? Observable Collection is the term I am looking for, I believe.
I hope someone is able to bridge the gap in my understanding of WPF (property) internals; I would very much appreciate it. A basic sample would be even better, but if it is too complicated, just a nudge in the right direction will do. Thank you. :)
It's been a while since I solved this, and while I cannot remember the exact cause of the problems, there were a few different issues that made up the bulk of the trouble I ran into.
I ended up making the Property in question a non-abstract DependencyProperty in the base class; it was the only way in which I could properly delegate the change notifications to the interface. Derived classes simply ended up binding it to their controls (with a proper Converter in the case extra logic was necessitated).
As Dictionary<string, BasePage> does not support any sort of change notification, I made an extra collection of ObservableCollection<BasePage> which I used for binding purposes.
However, such a collection does not propagate a change event when items inside of it has a property changed. Since this situation required that, and I was binding to the collection itself in the context of a property that does not have a Master<->Detail relationship like a DataGrid (which basically add their own OnPropertyChanged handlers to the binded object), I ended up subclassing a VeryObservableCollecton<>. This one listens to its own items, and throws a proper change event (I think it was an OnPropertyChanged from the INotifyPropertyChanged interface) so that the binding (or in this case a multi-binding) would refresh properly and allow my interface to update.
It is hardly the prettiest code, and it feels over-engineered, but at least it allows me to properly bind the UI to the data in this manner.