Michael
Michael

Reputation: 749

Property Getters and Setters when implementing INotifyPropertyChanged?

I am trying to implement INotifyPropertyChanged for a lot of classes, and each of these classes have lots and lots of properties. I have been following this MSDN documentation for how to implement INofifyPropertyChanged, but their instructions don't seem to be practical in cases where a class has many many properties.

Currently most of my properties use the short hand:

public DateTime? DateClosed { get; set; }

But the documentation says that i need to add the following to each setter method:

// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("DateClosed");

This means that I then need to declare a body for the get method and declare private variables to handle the actual getting and setting of properties. Like this:

private DateTime? _dateOfIncident = null;
public DateTime? DateClosed
{
    get { return _dateOfIncident; }
    set
    {
        _dateOfIncident= value;
        // Call OnPropertyChanged whenever the property is updated
          OnPropertyChanged("DateClosed");
    }
}

Does anyone know a way around this?

Upvotes: 5

Views: 3976

Answers (4)

Bart
Bart

Reputation: 10015

A few classes can easily be changed to implement INotifyPropertyChanged. But since you state you have a lot of classes with a lot of properties, it's a real burden to get this done manually or even with templates.

What you really need is a tool that does it for you, so I present you Fody and it's NotifyPropertyChanged plugin. What Fody does is weave some extra code in between your code at compile time. The only thing you have to do is add a single attribute on the classes you want to implement INotifyPropertyChanged and the rest is done for you.

[ImplementPropertyChanged]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

Upvotes: 5

scotty
scotty

Reputation: 1

Simply use the Observable Object class. Instead of creating a DateTime property, you'd create an ObservableObject<DateTime> and you would just bind to DateClosed.Value.

Upvotes: 0

Killnine
Killnine

Reputation: 5890

I'm not sure you're going to find a workaround here. Auto-properties, as you're using them now, are really just a compiler shorthand that get's converted to full properties with a backing field eventually anyway (at least, as I understand it).

The use of INPC is a routine that's sorta separate and apart from the duty of a normal property. It's notifying subscribers (usually, your view XAML) that the property in question has changed or is changing.

tl;dr -- you're not going to get around having to rewrite autoproperties to full properties with backing fields. But toolkits like MVVMLight have some great Visual Studio code snippets to make this relatively fast. Eventually you can even do this:

private string _someString;
public string SomeString
{
    get { return _someString;}
    set 
    {
            //Set returns bool, so you can trigger other logic on it! 
            Set(() => SomeString, ref _someString, value);
    }
}

This gives you some neat features:

  • Strong naming (unlike the magic string in your example)
  • Set only triggers INPC event if the value is different
  • Set returns boolean so you can perform more action if the value changed

MVVMLight is nice in that you don't have to use all its features, or even implement MVVM pattern. It just has a lot of nice 'tools' you can leverage.

Upvotes: 1

Ron Beyer
Ron Beyer

Reputation: 11273

There are a lot of patterns to do it, or you can buy a tool like PostSharp that will do it for you.

For example, here is one method of doing it:

public abstract class BaseNotifyPropertyChanged : INotifyPropertyChanged
{
    private Dictionary<string, object> _valueStore = new Dictionary<string, object>();

    public event PropertyChangedEventHandler PropertyChanged;

    protected T Get<T>([CallerMemberName]string property = null)
    {
        object value = null;
        if (!_valueStore.TryGetValue(property, out value))
            return default(T);
        return (T)value;
    }

    protected void Set<T>(T value, [CallerMemberName]string property = null)
    {
        _valueStore[property] = value;
        OnPropertyChangedInternal(property);
    }

    protected void OnPropertyChanged([CallerMemberName]string property = null)
    {
        OnPropertyChangedInternal(property);
    }

    private void OnPropertyChangedInternal(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Which you then inherit from your classes:

public class PlainOldObject : BaseNotifyPropertyChanged
{
    public int MyProperty
    {
        get { return Get<int>(); }
        set { Set(value); }
    }
}

Which takes care of the backing store and everything for you. You may want to add logic to only call the OnPropertyChangedInternal if the property actually changed (compare references or value), but I'll leave that as an exercise for you.

Upvotes: 0

Related Questions