Nicenis has moved to GitHub: https://github.com/Nicenis/Nicenis

Introduction

If you are a .NET developer, especially a WPF developer with MVVM, you must know the importance of the INotifyPropertyChanged interface. It is a core interface that is used almost every day. So if we refine the base implementation of INotifyPropertyChanged, even if the improvement is small, it is likely to lead to quite high productivity. The PropertyObservable class is a base implementation for INotifyPropertyChanged. It is designed to improve productivity as much as possible.

Basic INotifyPropertyChanged Implementation

INotifyPropertyChanged is a very simple interface. It declares only the PropertyChanged event:

// Notifies clients that a property value has changed.
public interface INotifyPropertyChanged
{
    // Occurs when a property value changes.
    event PropertyChangedEventHandler PropertyChanged;
}

The PropertyChanged event is easily implemented with a few lines of code like this:

public class PropertyObservable : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = PropertyChanged;
        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

But this is not the end of the story. You must write properties that raise the PropertyChanged event properly when they are changed.

Writing Properties

If you write a property with the basic INotifyPropertyChanged implementation, it will look like this:

public partial class Test : PropertyObservable
{
    int _number;

    public int Number
    {
        get { return _number; }
        set
        {
            if (_number == value)
                return;

            _number = value;
            OnPropertyChanged("Number");
        }
    }
}

Hereafter I’ll call it a raw property. The raw property is not bad but the raw property setter is a little verbose. The PropertyObservable class provides methods named SetProperty for this situation. The raw property setter can be rewritten with a SetProperty method like this:

public partial class Test : PropertyObservable
{
    int _number;

    public int Number
    {
        get { return _number; }
        set { SetProperty(ref _number, value, "Number"); }
    }
}

If you use the .NET framework 4.5 or higher, you can omit the property name like this:

public partial class Test : PropertyObservable
{
    int _number;

    public int Number
    {
        get { return _number; }
        set { SetProperty(ref _number, value); }
    }
}

Hereafter I will assume that you use the .NET framework 4.5 or higher for brevity. In most cases, the above property implementation is enough. It is fast, and is recommended for all uses. But it requires managing a private field. Besides, in my point of view it looks a little messy when multiple properties are defined with comments:

public partial class Test : PropertyObservable
{
    int _number1;

    /// <summary>
    /// The number 1.
    /// </summary>
    public int Number1
    {
        get { return _number1; }
        set { SetProperty(ref _number1, value); }
    }

    int _number2;

    /// <summary>
    /// The number 2.
    /// </summary>
    public int Number2
    {
        get { return _number2; }
        set { SetProperty(ref _number2, value); }
    }
}

Anyway, there is another SetProperty method that does not require a private field. The Number property can be rewritten without a private field like this:

public partial class Test : PropertyObservable
{
    public int Number
    {
        get { return GetProperty<int>(); }
        set { SetProperty(value); }
    }
}

The GetProperty method retrieves a property value that is stored in an internal storage. The above property implementation is slower than the previous property implementation, but in my point of view, it looks neat and is slightly more productive. The following example shows two properties with comments:

public partial class Test : PropertyObservable
{
    /// <summary>
    /// The number 1.
    /// </summary>
    public int Number1
    {
        get { return GetProperty<int>(); }
        set { SetProperty(value); }
    }

    /// <summary>
    /// The number 2.
    /// </summary>
    public int Number2
    {
        get { return GetProperty<int>(); }
        set { SetProperty(value); }
    }
}

If you need to set a default property value, you can use the getDefault parameter of the GetProperty method:

public partial class Test : PropertyObservable
{
    public int Number
    {
        get { return GetProperty<int>(getDefault: () => 7); }
        set { SetProperty(value); }
    }
}

Checking Property Changes

It is common to run custom code when a property is changed. If you need to know when a property is changed, the most famous way is to handle the PropertyChanged event:

public partial class Test : PropertyObservable
{
    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);

        if (e.PropertyName == "Number")
        {
            Debug.WriteLine("Number: " + Number);
            return;
        }
    }
}

But the PropertyChanged event has a flaw. It does not provide the old property value. Therefore PropertyObservable provides another event named PropertyValueChanged that provides the old property value:

public partial class Test : PropertyObservable
{
    protected override void OnPropertyValueChanged(IPropertyValueChangedEventArgs e)
    {
        base.OnPropertyValueChanged(e);

        if (e.PropertyName == "Number")
        {
            Debug.WriteLine("Number: {0} -> {1}", e.OldValue, e.NewValue);
            return;
        }
    }
}

But be warned, the event is only raised when you set a property value by a SetProperty method. Besides, the OldValue and NewValue are not type safe.

Sometimes it is reasonable to handle property changes in property setter for code manageability. In the raw Number property setter, the above code can be rewritten like this:

public partial class Test : PropertyObservable
{
    int _number;

    public int Number
    {
        get { return _number; }
        set
        {
            if (_number == value)
                return;

            int oldValue = _number;
            _number = value;
            OnPropertyChanged("Number");
            Debug.WriteLine("Number: {0} -> {1}", oldValue, value);
        }
    }
}

If you use a SetProperty method in the property setter, it can be simplified. The SetProperty methods return true if a property is changed. So, the above code can be rewritten like this:

public partial class Test : PropertyObservable
{
    public int Number
    {
        get { return GetProperty<int>(); }
        set
        {
            int oldValue = Number;
            if (SetProperty(value))
                Debug.WriteLine("Number: {0} -> {1}", oldValue, value);
        }
    }
}

Or you can use the onChanged parameter of the SetProperty methods. It is a callback delegate that is called when a property is changed. It also provides the old property value in its delegate parameter. So, you don’t need to cache the old property value:

public partial class Test : PropertyObservable
{
    public int Number
    {
        get { return GetProperty<int>(); }
        set
        {
            SetProperty(value, onChanged: p =>
            {
                Debug.WriteLine("{0}: {1} -> {2}", p.PropertyName, p.OldValue, p.NewValue);
            });
        }
    }
}

Adjusting Property Value

It is often required to adjust property value before it is set for fixing invalid input or other reasons. Adjusting property value is easy. For example, the following code shows a raw property setter that does not allow negative numbers:

public partial class Test : PropertyObservable
{
    int _number;

    public int Number
    {
        get { return _number; }
        set
        {
            if (_number == value)
                return;

            if (value < 0)
            {
                Debug.WriteLine("Number: {0} >> 0", value);
                value = 0;

                if (_number == value)
                    return;
            }

            _number = value;
            OnPropertyChanged("Number");
        }
    }
}

If you use a SetProperty method in the property setter, it will look like this:

public partial class Test : PropertyObservable
{
    public int Number
    {
        get { return GetProperty<int>(); }
        set
        {
            if (Number == value)
                return;

            if (value < 0)
            {
                Debug.WriteLine("Number: {0} >> 0", value);
                value = 0;
            }

            SetProperty(value);
        }
    }
}

The SetProperty methods also provide the onChanging parameter that is called before a property is changed. It enables you to override the new property value by its delegate parameter. So, you can use the onChanging parameter like this:

public partial class Test : PropertyObservable
{
    public int Number
    {
        get { return GetProperty<int>(); }
        set
        {
            SetProperty(value, onChanging: p =>
            {
                if (p.NewValue < 0)
                {
                    Debug.WriteLine("{0}: {1} >> 0", p.PropertyName, p.NewValue);
                    p.NewValue = 0;
                }
            });
        }
    }
}

If you want to adjust property values in a centralized location, the PropertyValueChanging event can be used like this:

public partial class Test : PropertyObservable
{
    protected override void OnPropertyValueChanging(IPropertyValueChangingEventArgs e)
    {
        base.OnPropertyValueChanging(e);

        if (e.PropertyName == "Number")
        {
            if ((int)e.NewValue != Number && (int)e.NewValue < 0)
            {
                Debug.WriteLine("Number: {0} >> 0", e.NewValue);
                e.NewValue = 0;
            }

            return;
        }
    }
}

But be warned, like the PropertyValueChanged event, the PropertyValueChanging event is only raised when you set a property value by a SetProperty method. Besides, the OldValue and NewValue are not type safe. And the PropertyValueChanging event is raised before the onChaning callback is called.

Supported Preprocessor Symbols

You can use the following preprocessor symbols:
  • NICENIS_4C: Define this symbol if you want to compile with the .NET Framework 4 Client Profile.

Last edited Dec 31, 2016 at 8:15 PM by Ryeol, version 6

Comments

No comments yet.