28

I have core functionality encapsulated in ViewModelBase

Now I want to see when PropertyChanged event was raised by ViewModelBase and act on it. For example, when one property was changed on ViewModelBase - I want to change property on my ViewModel

How do I achieve this?

public class MaintainGroupViewModel : BaseViewModel<MEMGroup>
    {


public abstract class BaseViewModel<T> : NotificationObject, INavigationAware
        where T : Entity
    {
katit
  • 17,375
  • 35
  • 128
  • 256
  • 1
    Does your `NotificationObject` implement `INotifyPropertyChanged` interface? If so, then it is basic event subscription. If not then you should implement `INotifyPropertyChanged`. – Anas Karkoukli Oct 19 '11 at 16:53

3 Answers3

83

Usually I use register to the PropertyChanged event in the class Constructor

public MyViewModel()
{
    this.PropertyChanged += MyViewModel_PropertyChanged;
}

and my PropertyChanged event handler looks like this:

void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "SomeProperty":
            // Do something
            break;
    }
}
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Is there anyway to get rid of "magic-strings" while handling. (I am not using C# 6) – Marshal Apr 12 '17 at 19:44
  • @Marshal I'm not sure, what about [this blog post by Josh Smith](https://joshsmithonwpf.wordpress.com/2009/07/11/one-way-to-avoid-messy-propertychanged-event-handling/)? – Rachel Apr 13 '17 at 14:52
  • I think that's a good solution, and testable enough. Thanks for pointing to the article. – Marshal Apr 13 '17 at 15:39
  • @Rachel: What do you think about using this technique to also avoid "Duplicate RaisePropertyChanged for Cascading Read-Only Properties" ("https://stackoverflow.com/questions/46314677/avoiding-duplicate-raisepropertychanged-for-cascading-read-only-properties")? – Tom Sep 21 '17 at 00:44
  • 1
    @Tom I think it's fine. Left you an answer to your question to :) – Rachel Sep 22 '17 at 14:01
15

I am concerned that you're effectively doing a 'manual binding' (bad) for a property in a derived class to a value on the base class (also bad). The whole point of using inheritance is that the derived class can access things in the base class. Use a protected modifier to indicate things should only be accessible to derived classes.

I would suggest this (potentially) more correct method:

Base class:

protected virtual void OnMyValueChanged() { }

Derived class:

protected override void OnMyValueChanged() { /* respond here */ }

Really, subscribing to an event in the base class of the very class you're writing just seems incredibly backwards - what's the point of using inheritance over composition if you're going to compose yourself around yourself? You're literally asking an object to tell itself when something happens. A method call is what you should use for that.

In terms of "when one property was changed on ViewModelBase - I want to change property on my ViewModel", ... they are the same object!

Kieren Johnstone
  • 41,277
  • 16
  • 94
  • 144
  • Yes, it does smell :) My scenario is like this... I have base functionality but in this specific case I want to rearrange some of the data on my ViewModel when some base data changed. Base class maintains main data entity (let's say Customer) and I want to update more UI when Customer changes. – katit Oct 19 '11 at 17:02
  • I would still suggest using methods rather than events, and using composition over inheritance if necessary: create a new object which contains the base ViewModel. Otherwise everyone will still have access to the 'base' functionality of the base ViewModel, which actually you seem to be modifying, so a derived class doesn't fit. – Kieren Johnstone Oct 19 '11 at 17:05
  • Maybe method is a way to go. Another example. Base class maintains "state" of my data entry form. Like NEW, EDIT, ADD. Bunch of UI binds to this state. In my VM, I want to know about this state change as well. I can create method OnStateChange or I can subscribe to PropertyChanged. Do you see need to recompose or just create methods? Methods might be "cleaner" to look at – katit Oct 19 '11 at 17:08
  • That functionality would belong in the base view model - especially something as generic as OnStateChange. Can you give another example? :) – Kieren Johnstone Oct 19 '11 at 17:09
  • Yes, there is a lot of functionality in BaseVM. But same OnStateChange might be needed in VM. Let's say I want to reset lookup list when new record being created. I need to know when state changes and do this. – katit Oct 19 '11 at 17:13
  • As I said OnStateChange would belong either in the base VM. If the base VM isn't appropriate, then you'd create a VM to represent the view that needs the new functionality. The 'inner VM' holding the data would be a property on and component of the new VM. The new VM might then subscribe to events. That's composition though, inheritance simply doesn't work based on what you're saying.. – Kieren Johnstone Oct 19 '11 at 17:15
  • 1
    I got it. Basically, subscribing to own events is a code smell. Overriding OnStateChange and calling base.OnStateChange is a proper way to handle this scenario. Both kind of achieve same functionality. – katit Oct 19 '11 at 17:17
  • @katit: This is how the framework, er, works. Check out many of the events in various framework classes. You'll see that they have "On[EventName]" virtual methods you can override to interact with the raising of the event. There's more to it, of course... –  Oct 19 '11 at 17:32
1

The direct way to subscribe to property changes is using INotifyPropertyChanged if your BaseViewModel implements it:

PropertyChanged += (obj, args) =>
   { System.Console.WriteLine("Property " + args.PropertyName + " changed"); }

If it doesn't, then it has to be a DependencyObject, and your properties have to be DependencyProperties (which is probably a more complicated way).

This article describes how to subscribe for DependencyProperty changes.

Vlad
  • 35,022
  • 6
  • 77
  • 199