We have a number of data objects that realize INotifyPropertyChanged to allow for WPF Binding updates. There are also a number of places where our code subscribes to PropertyChanged because we're interested in some value updates.
This results in pretty ugly code where we need to check which property actually changed (we do this using Expressions so it's always type/refactor safe).
Is the preference to raise a specific event (PriceChanged etc...) for when we want to subscribe to it, or hook into PropertyChanged and check the property name?
If a number of properties you want to subscribe to is not very big, I'd create dedicated events as they are better in terms of readability and discoverability.
However, if there are quite a few properties the answer is not so obvious. I usually try to avoid such situations by applying Observer Synchronization pattern (subscribing to Model changes rather than ViewModel). It helps me keep VMs thin.
Related
I am looking into the topic why a ObservableCollection/ListCollectionView/CollectionView raises a NotSuportedException when calling the CollectionChanged with the parameter of IList.
//Throws an exception
private void collectionChanged_Removed(IList items)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
}
I have found several Webpages, talking about this topic and they suggest either using the Reset ability to force a complete redraw of the UI, or just simply call for each item the CollectionChanged or some more creative way: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx
I just cant find the WHY?
For me it makes no sense why this would be the case.
Is there any chance that this lacking feature, which we all face at some point in our Development Cycle, since the Add method just has to much of an overhead when you want to Add multiple items fast, will be implemented any time (.Net 5, C# 6...).
Edit:
In my specific case, I have written my own class :
public class ObservableList<T> : IList<T>, IList, IEnumerable<T>,
INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
//other stuff...
}
And still throws the said NotSupportedException.
Inspired by VirtualBlackFox's answer I took a look under the hood of the CollectionView classes in ILSpy. It appears that the primary reason why the lack of support for Range operations is because internally the CollectionView uses a change log to centrally manage pending changes of all kinds and dispatch messages on a per/item basis.
By its very purpose, the CollectionView could store 1000s of records used simultaneously with multiple UI controls representing its underlying data. So, adding or deleting records must be done on an atomic basis to maintain the integrity of the UI controls that access the view information. You can't synchronize incremental changes with multiple UI subscribers using bulk change events without passing the grouping, sorting, and filtering functionality of the CollectionView onto the UI controls that use it.
The CollectionView also derives from System.Windows.Threading.Dispatcher so the issue may be co-related to how it manages work items on it's thread. The call path includes a protected ProcessCollectionChanged method that specifically processes individual changes on the UI thread. So, updating ranges may interfere with the whole threading model it uses to interact with UI elements that use it.
I totally agree that having consumers of the CollectionView pass in an IList to NotifyCollectionChangedEventArgs is silly. It specifically rejects anything with a length != 1 and hard-codes for args.NewItems[0] internally.
As #nmclean said in the comments the problem isn't in the collection emitting CollectionChanged but on the receiving end.
If you look at the code of ListCollectionView (Using DotPeek for example or on new versions of visual studio you can access the reference source code) you will notice that each time the attached collection change it call a method ValidateCollectionChangedEventArgs that throw when there is more than one element changed
private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewItems.Count == 1)
break;
else
throw new NotSupportedException(System.Windows.SR.Get("RangeActionsNotSupported"));
...
The rest of the class and it's CollectionView base class are already big beasts (2710 and 2027 lines in the source published on the reference source code) so Microsoft might have wanted to avoid supporting a complex case that the collection that they recommend don't create anyway.
(The method handling collection change is already 141 lines in the reference source code and adding multi element support will make it grow even more or need a careful split and potentially break other things...)
I didn't find any suggestions linked to adding support for range events in Microsoft Connect but you should submit your own if it is important for you.
I guess this is mainly for performance reasons. I was also shocked when I saw that CollectionView also does not accept the -1 value for NewStartingIndex or OldStartingIndex. So basically, CollectionView always wants a mapping from items to their indices. However, it does not require this mapping to be exact (which is strange from my point of view), it is allowed that NewStartingIndex is smaller than the correct index (unless it is -1).
I think the root of the problem is that large parts within Microsoft still think that a list is the one and only way to implement a collection, which of course just is not true. Here, the creators of NotifyCollectionChangedEventArgs thought about alternatives (such as linked lists or hashing collections), but the UI guys did not. Or at least they did not want to support these collections as they appear rather rarely.
The temporary solution is useless. It only hides the problems.
The solution could lie in making events where you provide an entire new list to the observers. That way Microsoft won't have to implement efficient range handlers for each type of observer.
Basically, I was always in the understanding that you should return the expose base types whenever you can and worry about implementation details internally, which makes sense...
But, I'm not sure what to do here. Basically, right now I have:
ReadOnlyObservableCollection<Foo> MyFoos {get; private set; }
I'm wondering if that should be returned as a ReadOnlyCollection<Foo> or an ICollection<Foo> because internally I never really use any observable parts or attempt to write to the collection. WPF seems to not care what I return, it still binds it and triggers the collection changed notification event properly. But, I read somewhere that I should design this to really have any consuming view handle my ViewModel.
So I'm a bit torn here. I'm thinking that leaving it as a ReadOnlyObservableCollection<T> makes the most sense to explicitly tell the consuming view what they can and can not do with the property, but I'm also under the impression that you should reduce down types to their base types when you can. So I'm not sure what to do here. Especially with the fact that WPF doesn't care what type I return, it figures out that it's observable.
I would probably leave it as ReadOnlyObservableCollection because that very specifically states what a consumer of your ViewModel is allowed to do with your collection. Also note that WPF doesn't actually bind directly to your collection, it binds to the return value of CollectionViewSource.GetDefaultView, which returns an ICollectionView. ICollectionView has INotifyCollectionChanged in its contract.
From a performance perspective, you would want to at least use an items source as a collection that implements INotifyCollectionChanged. MVVM provides a lot of benefits but is primarly concerned with unit testing and separation of concerns, so that choice of whether to use a ReadOnlyObservableCollection or an interface like ICollection{T} would be based on your unit testing goals.
I wasn't able to find a discussion on this topic but I think its pretty important so theres a big chance it exists, so Ill be gladful to read.
In our viewmodel setters for properties we can check if provided value is equal to current value and only notify in that case, or we can ignore the check and set & notify right away.
While this check appears pleasing as it saves some resources when "refresh" on VM doesn't cause View redraw it can also present some bad effects when View is a little more complicated and has some code behind which either can't be put in VM or when putting it in VM is too difficult compared to leaving it in View.
So generally from my experience with MVVM I can think of scenario when this check prevented some code from working as expected. So currently I am thinking that this check is a dangerous thing and generally not worth the benefit of a saving redraw sometimes.
Also sometimes this check can be used as a substitute for better program logic when for example there are 2 properties on VM that actually are closely connected like, say string & object representation of some entity. So when one is set for example string, inside it calls an object setter too if validated correctly, and this equality check saves situation from infinite loop of object <-> string setters, but in that case I think its much better to introduce a flag that is checked in setter and doesn't call another if it is not needed, when call to say object setter came from call to string setter.
I think that this check is mostly used for scenario I just described which I think isn't exactly a good reason to perform the check.
So how should we code our setters?
Edit: well just to give a scenario, the simplest and most stupid one would be this: if we have a OneWay binding to View from VM and for some reason there exists some handler in View codebehind that changes View directly, causing it to become unsynchronized with VM, refreshing VM wont help. Of course its obvious that in that case we should be calling VM setter that is bound , but I think in some cases scenario can be more complicated and advanced where resolution becomes less transparent. So scenario is stupid but having a check would prevent from correct behavior. Ill think more on a real world example where its not so easy to resolve but probably will require some time..
In my personal experience I have found this check very useful and I always put the equality check in the setter to make sure that if there is no need to refresh the binding value then I should not call the property change and should not force view to refresh the value when it is not required at all.
Talking about your scenario where equality check is causing you trouble, I think there must be something odd in the scenario or most probably you might be approaching something incorrectly otherwise I have used MVVM a lot and haven't find such a scenario in my case. Even if there exists a scenario where it may might make sense to not to do equality check, I won't stop using equality checking because of a small minor issue I am facing in some few cases. I will try to do some workaround in that particular problematic case to avoid the problem rather then not using the equality check in the setters
in general cheking is good idea especially when you're firing some event when value is changed it prevents you from doing some unneeded or even unwanted job to be done.
I don't see why woud u want to fire event when value is the same. If u want to, then its better to fire it manually then doing something that someone else might not expect.
I have a class with a method, Register that subscribes to a number of events on classes that it contains, using the standard aClass.SomeEvent += the_handler. This class also has an Unregister method that unsubscribes from these events using -=. This works just fine but we're finding that if we add a new event to subscribe to that it's very easy to forget to include the unsubscription in Unregister. This manual method of maintaining event subscriptions is proving to be fragile.
Is there a way to maintain a list of subscriptions that can be iterated over and unsubscribed from dynamically? (And potentially iterate over and re-subscribe when calling Register after Unregister).
Some details: The class has a reference to 3 other classes (currently, but not definitively limited to 3), the various events on these classes are all of type EventHandler or EventHandler<T>.
how about getting invocation list from the EventHandler.GetInvocationList() and then ierate through and manually remove/unregister them ? note, you only have access to the GetInvationList() method from the class that has that EventHandler, so you might need to expose a method UnregisterAll() to make sure it removes all the delagates in the event invocation list
you can also make sure your class inherits IDisposable and with using(){ } it will call Dispose which will clean up all subscribers
After a thorough look through SO I found this answer:
C# Dynamic Event Subscription
That does what I want (almost). I don't like having to name events using strings as and such I won't be pursuing this design any further. Even though it's not the design I want, the answer shows a very useful method to achieve the desired behaviour and as such I'm marking this as accepted.
I'm cutting my teeth on events and delegates today and to do so, I have been toying with the idea of experience bars, those progress bars from games. But I have a question about the better way to solve my problem - it could be as simple as bad design. Let me provide you some details.
I have modelled my idea with an ExperienceBar class.
It contains properties:
int StartValue
int CurrentValue
int EndValue
and a method
void UpdateBar(int)
UpdateBar adds the parameter to CurrentValue and then tests to see if it has reached EndValue. If it exceeds the amount, the EndValue increases and the amount continues on. Note that initially in my thinking, it is not concerned with the effects of reaching the maximum amount possible, just that the end value increases and the StartValue is reset to zero.
Another class called Player has a property of class ExperienceBar.
In my little demo, when Player.ExperienceBar.UpdateBar(int) reaches the EndValue it fires an event which is handled by the Player class. It updates the Player.Level property by one.
I've just realised that I could achieve the same thing by just changing UpdateBar(int) to return type "true". This method could be tested by the Player class and when true, Player.Level increases by one.
So my question - which is the best practice way to handle this rather specific circumstance? As a general rule of thumb for these kind of situations, is it better to handle events, or is it better just to keep it simple with the testing of return statements?
PS: I hope I've made this clear as possible, but I can try to clarify if anyone is having trouble. I believe there may be some redundancies already with my idea, but try not to deviate from the question please. I'm kind of aware of them! Thank you :)
Well... To me, events is the good way to do it.
However, if I was to design the application it would be down to one question: Will the ExperienceBars's event when it reaches EndValue ever be used by anyone else than the class calling UpdateBar.
If you are designing a component to be used in many places (which seems to be the goal), the answer to me seems to be an almost certain yes, therefore my answer is use events!
/Victor
In my opinion, there's no best way to do this. There are various ways to implement the class that, depending on how it is going to be used, are a better or worse fit.
Use events when you want to implement the observer pattern for many "clients" or "observers" who need to know the state of an object and need to be alerted when that state changes. this works for the degenerate case where there is only one client, but the caller of the the method that changes the object's state is not the one that needs to know about the change.
Use return values when the state only needs to be known by the caller, there are no other observers of the class. This is simple, and limits the scope of the knowledge of the state of the class to the item that immediately needs to know it.
And finally, do not over-design this. If it only needs to notify the caller, do not implement events. If at some later date the class needs to be "observed" then implement events at that point.
It all depends on the coupling of your components and the flow of your program. The downside to events is that you will increase the complexity of your program, because it is harder to trace exactly what the flow of execution will be when any piece of code can subscribe to your event. The upside is it allows for a more flexible and scalable design, since any piece of code can subscribe to your event.
So here is the thing, if Player is going to be in charge of handling all things related to leveling up, then having a tight coupling between Player and ExperienceBar is ok. Let's say you want to expose an AddIn framework, in that case you probably want to expose leveling up to external plugins, in which case an event makes a lot more sense.
Personally, I would have XP be a part of Player, and have Player expose a LevelUp event, but I don't know if that would be a good idea for you and your framework/domain modeling without seeing your existing code.
I would use events rather than a return value. Why? Two reasons:
What does returning true mean when returning from UpdateBar? That it was updated? That xyz happened? Someone else looking at this (or you, two months down the road) will wonder as well.
What if more than one thing should occur when the limit is reached? Then you have to tie all of the code related to those things (levelling, getting a new item, whatever) into the method that you used to update the bar in the first place.
I would have an event associated with reaching a certain level and then "listeners" for that event that can respond accordingly.
I don't think it makes sense to have Experience bar fire an event - in that case a return value would be fine. It could then call the Player's LevelUp function, which could fire an OnLevelUp event from the Player class, if needed.