2

I've found that typing and re-typing the same property getters and setters that raise the property changed event to be tedious and also potentially error-prone because property names are passed as typed strings. After searching for an easier way to do that, I created the following generic class:

public class ObservableProperty<T> : INotifyPropertyChanged
{
    private T _backingField;
    public T Value
    {
        get { return this._backingField; }
        set
        {
            if (!EqualityComparer<T>.Default.Equals(this._backingField, value))
            {
                this._backingField = value;
                this.TriggerPropertyChangedEvent();
            }
        }
    }

    /// <summary>
    /// Creates an observable property with a default initial value.
    /// </summary>
    public ObservableProperty() : this(default(T)) { }

    /// <summary>
    /// Creates an observable property with the specified initial value.
    /// </summary>
    /// <param name="initialValue">The value to initialize the observable property to.</param>
    public ObservableProperty(T initialValue)
    {
        this.Value = initialValue;
    }

    #region INotifyPropertyChanged Implementation
    public event PropertyChangedEventHandler PropertyChanged;

    protected void TriggerPropertyChangedEvent([CallerMemberName]string propertyName = null)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion // INotifyPropertyChanged Implementation
}

Now instead of implementing INotifyPropertyChanged on the entire ViewModel and typing out all backing fields and property getters and setters long-hand, all that has to be done is:

public ObservableProperty<string> BindableText { get; private set; }

And then in the constructor:

this.BindableText = new ObservableProperty<string>("Default Text");

The view will now bind to BindableText.Value in the XAML.

So each property will have its own implementation of INotifyPropertyChanged, becoming a mini view model on its own, and the models and view models that create instances of these will not need to implement the interface at all.

This works fine and properties that can be bound to are very easy to create. ViewModels can even reference observable properties that are on the models like:

public ObservableProperty<int> MyNumber { get { return MyModel.Number; } }

so that views do not bind directly to properties on models, only indirectly through their view models.

The question I have, since it's not something I have a lot of experience with, is will there be any potential soft memory leaks by taking this approach that I could take measures against somehow?

OurManFlint
  • 79
  • 2
  • 9
  • 4
    Memory leaks are almost impossible to detect just by looking at code, you'd need to profile your application in order for you to really see if it is indeed leaking memory. – Michel de Nijs Mar 12 '15 at 14:22
  • There is a better approach to avoiding typos in `INotifyPropertyChanged` that involves Expressions, something like this http://stackoverflow.com/questions/2711435/typesafe-notifypropertychanged-using-linq-expressions – JustSomeGuy Mar 12 '15 at 14:22
  • 2
    You may want to use the parameter attribute `CallerMemberName` to let the compiler insert the property names for you. – Binkan Salaryman Mar 12 '15 at 14:28
  • The ObservableProperty class does already use the CallerMemberName attribute in its implementation of INotifyPropertyChanged. I began using this before I wrote the class but writing out all the property getters and setters was still tedious. – OurManFlint Mar 12 '15 at 14:37
  • Michel de Nijs, thank you. You are correct. However, I wondered if there was a 'defensive programming' approach to ensure that all subscribers are unsubscribed from these properties so that it's never an issue anyway. – OurManFlint Mar 12 '15 at 14:39
  • If this leaks, then why wouldn't every view model/class that implements INPC leak? The question doesn't make any sense. –  Mar 12 '15 at 14:40
  • That's kind of what I meant, Will. If implementing INPC the standard way is fine, will *this* approach be any different? – OurManFlint Mar 12 '15 at 14:44
  • This is a type. That implements INPC. Why would this type implementing INPC be any different than any other type implementing INPC? It's just a class with an event. So the rules here are no different than the rules for any other type that has an event. You've created a type like any other type. So, what's the problem here? –  Mar 12 '15 at 14:46
  • If there's no problem then you've answered my question. Thanks. – OurManFlint Mar 12 '15 at 14:48
  • "The ObservableProperty class does already use the CallerMemberName attribute" -- so why waste your time worrying about this design? `[CallerMemberName]` is more reliable, way more concise, and proven to work. Why not just go back and remove this implementation from any class that uses it and replace it with the standard `[CallerMemberName]`-based style? – Peter Duniho Mar 12 '15 at 15:13
  • The class I wrote is meant to reduce: private int _number; public int Number { get { return this._number; } set { if (this._number != value) { this._number = value; this.TriggerPropertyChangedEvent(); } } } for every single property that is going to be bound to by the view to: public ObservableProperty Number { get; private set; } not to replace CallerMemberName. Whether the class uses CallerMemberName doesn't matter because its property will always be called 'Value'. – OurManFlint Mar 12 '15 at 15:25
  • By the way, C# 6.0 introduces a new `nameof()` operator. This solves your problem with strings as property names. – dymanoid Mar 14 '15 at 19:20
  • This is SOOOO WRONG. How do i listen for all property changes of a ViewModel then? I implore you not to take this route. Just use a code snippet to generate the property...You can use Expression Tree parsing and Lambdas to get the property name in a strongly typed http://www.codeproject.com/Tips/412985/Simplifying-MVVM-Properties – atomaras Oct 04 '15 at 17:37
  • @atomaras I just saw your comment. I'm not sure how you mean that it's wrong. I've been using this class I made since I wrote it and posted this question without problems. The issue here was not in getting the property name. Using this class, the property is always called "Value" so it doesn't really matter. – OurManFlint Jan 12 '16 at 17:07

1 Answers1

0

is will there be any potential soft memory leaks by taking this approach that I could take measures against somehow?

Risk of getting a memory leak is just the same as with the standard INotifyPropertyChange approach:

  1. No memory leaks if these ObservableProperties are used for bindings in XAML, because WPF uses Weak Events for binding properties of objects that implement INotifyPropertyChange interface.

  2. Can be memory leaks if these ObservableProperties are used in code to subscribe for INotifyPropertyChange events.

For example, if ViewModel subscribes for INotifyPropertyChange event of such an ObservableProperty object of a Model then this ViewModel will not be garbage collected until Model is garbage collected or ViewModel unsubscribes. This leak can be noticeable if many ViewModels are created for one Model.

Lightman
  • 1,078
  • 11
  • 22