Click here to Skip to main content
16,017,954 members
Articles / Desktop Programming / XAML
Tip/Trick

Property Change Notification with Compile Time Safety

Rate me:
Please Sign up or sign in to vote.
3.79/5 (5 votes)
9 Dec 2014CPOL 34.5K   7   27
Simple trick to provide compile time safety for client side classes using INotify to notify when property changes (used generally by XAML bindings)

Introduction

Here is a cool little trick to ensure compile time safety when raising property change notification events. Instead of using strings as in "PropertyName", we can very well use a lamda expression like () => PropertyName.

Background

Property change notification is generally used in applications where XAML binding is used and hence these events are using to ensure two way binding with the UI as in MVVM design pattern.

Using the Code

Below is a typical implementation of INotifyPropertyChanged interface which provides no compile time safety and client object passes string to notify of a change in the property:

C#
public class SampleClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    
    private string _name;
    
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value) return;
            _name = value;
            OnPropertyChanged("Name");
        }
    }
    
    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (propertyName != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

So, in order to ensure compile time safety, we provide a simple overload for OnPropertyChanged method as below, in turn using a helper method, which can very well be hosted in its own helper class. For the purpose of this example, I have it below:

C#
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
    OnPropertyChanged(ExtractPropertyName(propertyExpression));
}

private string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
    return (((MemberExpression)(propertyExpression.Body)).Member).Name;
}

And now, the client properties can be changed to raise the events like this:

C#
public string Name
{
    get { return _name; }
    set
    {
        if (_name == value) return;
        _name = value;
        OnPropertyChanged(() => Name);
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWhy not use .net 4.5 and do this? Pin
tlford6512-Dec-14 5:54
professionaltlford6512-Dec-14 5:54 
AnswerRe: Why not use .net 4.5 and do this? Pin
FantasticFiasco15-Dec-14 22:25
FantasticFiasco15-Dec-14 22:25 
GeneralRe: Why not use .net 4.5 and do this? Pin
Gary Wheeler16-Dec-14 0:09
Gary Wheeler16-Dec-14 0:09 
GeneralRe: Why not use .net 4.5 and do this? Pin
FantasticFiasco16-Dec-14 2:15
FantasticFiasco16-Dec-14 2:15 
AnswerRe: Why not use .net 4.5 and do this? Pin
William E. Kempf16-Dec-14 7:41
William E. Kempf16-Dec-14 7:41 
QuestionRe - Use Stack Trace (Debug & Release Builds) Pin
TSFolkes11-Dec-14 4:55
TSFolkes11-Dec-14 4:55 
AnswerRe: Re - Use Stack Trace (Debug & Release Builds) Pin
William E. Kempf17-Dec-14 9:46
William E. Kempf17-Dec-14 9:46 
GeneralRe: Re - Use Stack Trace (Debug & Release Builds) Pin
TSFolkes17-Dec-14 19:36
TSFolkes17-Dec-14 19:36 
GeneralRe: Re - Use Stack Trace (Debug & Release Builds) Pin
William E. Kempf18-Dec-14 5:45
William E. Kempf18-Dec-14 5:45 
GeneralRe: Re - Use Stack Trace (Debug & Release Builds) Pin
TSFolkes18-Dec-14 5:58
TSFolkes18-Dec-14 5:58 
GeneralRe: Re - Use Stack Trace (Debug & Release Builds) Pin
William E. Kempf18-Dec-14 11:14
William E. Kempf18-Dec-14 11:14 
QuestionMinor enhancement Pin
Vijay Gill10-Dec-14 5:44
professionalVijay Gill10-Dec-14 5:44 
AnswerRe: Minor enhancement Pin
Aurimas10-Dec-14 9:23
Aurimas10-Dec-14 9:23 
GeneralRe: Minor enhancement Pin
Vijay Gill10-Dec-14 20:36
professionalVijay Gill10-Dec-14 20:36 
AnswerRe: Minor enhancement Pin
Abhishek Shrivastava10-Dec-14 10:37
Abhishek Shrivastava10-Dec-14 10:37 
GeneralRe: Minor enhancement Pin
Vijay Gill10-Dec-14 20:38
professionalVijay Gill10-Dec-14 20:38 
GeneralRe: Minor enhancement Pin
William E. Kempf16-Dec-14 7:45
William E. Kempf16-Dec-14 7:45 
QuestionLook at MVVM Light framework Pin
Aurimas9-Dec-14 23:51
Aurimas9-Dec-14 23:51 
AnswerRe: Look at MVVM Light framework PinPopular
Sacha Barber10-Dec-14 0:13
Sacha Barber10-Dec-14 0:13 
You dont need a framework for that, you just need a good base INPC class, something like this:

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq.Expressions;

using Moneycorp.Options.Client.ExtensionMethods;

namespace Moneycorp.Options.Client.ViewModel
{

    public class INPCBase : INotifyPropertyChanged
    {
        /// <summary>
        /// Warns the developer if this object does not have a public property with the specified name. This
        /// method does not exist in a Release build.
        /// </summary>
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void VerifyPropertyName(string propertyName)
        {

            // Verify that the property name matches a real,
            // public, instance property on this object.
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: " + propertyName;

                if (this.ThrowOnInvalidPropertyName)
                    throw new Exception(msg);

                Debug.Fail(msg);
            }
        }

        /// <summary>
        /// Returns whether an exception is thrown, or if a Debug.Fail() is used when an invalid property name is passed to the VerifyPropertyName method.
        /// The default value is false, but subclasses used by unit tests might override this property's getter to return true.
        /// </summary>
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }


        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(PropertyChangedEventArgs args)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, args);
            }
        }

        //Used by code that may want to fire additional properties. For example
        //when there is only a getter available, but the UI still needs to be notified


        /// <summary>
        /// Used by code that may want to fire additional properties. For example
        /// when there is only a getter available, but the UI still needs to be n
        /// </summary>
        /// <param name="propertyName">Expression representing the property to use</param>
        public void RaisePropertyChanged<TValue>(Expression<Func<TValue>> propertySelector)
        {
            if (PropertyChanged != null)
            {
                NotifyPropertyChanged(new PropertyChangedEventArgs(propertySelector.GetPropertyName()));
            }
        }

        public TRet RaiseAndSetIfChanged<TRet, TValue>(ref TRet backingField, TRet newValue, Expression<Func<TValue>> propertySelector)
        {
            if (EqualityComparer<TRet>.Default.Equals(backingField, newValue))
            {
                return newValue;
            }

            backingField = newValue;
            RaisePropertyChanged(propertySelector);
            return newValue;
        }

        public TRet RaiseAndSet<TRet, TValue>(ref TRet backingField, TRet newValue, Expression<Func<TValue>> propertySelector)
        {
            backingField = newValue;
            RaisePropertyChanged(propertySelector);
            return newValue;
        }





    }
}






Which allows this:


C#
public bool IsSelected
{
    get { return this.isSelected; }
    set
    {
        RaiseAndSetIfChanged(ref this.isSelected, value, () => IsSelected);
    }
}


Which as you say is 1 line, and not a single framework in sight
GeneralRe: Look at MVVM Light framework Pin
Aurimas10-Dec-14 9:18
Aurimas10-Dec-14 9:18 
GeneralRe: Look at MVVM Light framework Pin
Abhishek Shrivastava10-Dec-14 10:40
Abhishek Shrivastava10-Dec-14 10:40 
Suggestion[My vote of 2] CallerMemberName Pin
Rockair9-Dec-14 23:21
Rockair9-Dec-14 23:21 
GeneralRe: [My vote of 2] CallerMemberName Pin
Member 29250119-Dec-14 23:31
Member 29250119-Dec-14 23:31 
GeneralRe: [My vote of 2] CallerMemberName Pin
FantasticFiasco15-Dec-14 22:20
FantasticFiasco15-Dec-14 22:20 
GeneralRe: [My vote of 2] CallerMemberName Pin
William E. Kempf17-Dec-14 9:47
William E. Kempf17-Dec-14 9:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.