#1,205 – Use CallerMemberName Attribute to Make INotifyPropertyChanged Implementation Cleaner

The INotifyPropertyChanged interface is central to using data binding in WPF. You typically create ViewModels containing properties that fire a PropertyChanged event whenever a property value changes. Implementing this plumbing over and over for every property can become tedious. This argues for a reusable pattern to make the per-property code cleaner.

Specifying string-based property names when raising the PropertyChanged event can also be error-prone. When, passing property names as string literals, a misspelled property name doesn’t lead to a compiler warning, but just a quiet data binding failure.

Below is a pattern you can use to make property change code cleaner. It relies on the CallerMemberName attribute, which can be used to default a method parameter to the name of a caller–the property name in this case.

We have a generic method that compares a new property value to the current value in the backing variable. If the value has changed, it assigns the new value and fires the property changed event.

        protected bool SetProp<T>(ref T backingField, T value, [CallerMemberName] string propName = null)
        {
            bool valueChanged = false;

            // Can't use equality operator on generic types
            if (!EqualityComparer<T>.Default.Equals(backingField, value))
            {
                backingField = value;
                RaisePropertyChanged(propName);
                valueChanged = true;
            }

            return valueChanged;
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected void RaisePropertyChanged(string propName)
        {
            if (!string.IsNullOrWhiteSpace(propName) && (PropertyChanged != null))
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

A standard property implementation can now look pretty clean. (Note: We’re not checking the return value of SetProp, but we could do that if we wanted to perform other logic when the property value changes).

        private Dog _selectedDog;
        public Dog SelectedDog
        {
            get { return _selectedDog; }
            set { SetProp(ref _selectedDog, value); }
        }

You would generally put this sort of code in a common base class that all of your ViewModel classes could inherit from.

Advertisement

#395 – Rich ListBox Content using Data Binding, part I

In talking about the SnapsToDevicePixels property, I used as an example a ListBox that contained a list of movies.  Each entry in the ListBox had a number of data items related to the movie, including a thumbnail.

Let’s look at how to create this in WPF.  To start with, we need a class that stores information about an individual movie.  We’re going to use data binding to bind to instances of this class, and we want the binding to update when elements of the class change, so we implement the INotifyPropertyChanged interface.

    public class Movie : INotifyPropertyChanged
    {
        private string title;
        public string Title
        {
            get { return title; }
            set
            {
                if (value != title)
                {
                    title = value;
                    OnPropertyChanged("Title");
                }
            }
        }

        public int year;
        public int Year
        {
            get { return year; }
            set
            {
                if (value != year)
                {
                    year = value;
                    OnPropertyChanged("Year");
                }
            }
        }

        private Uri image;
        public Uri Image
        {
            get { return image; }
            set
            {
                if (value != image)
                {
                    image = value;
                    OnPropertyChanged("Image");
                }
            }
        }

        private string actorLead;
        public string ActorLead
        {
            get { return actorLead; }
            set
            {
                if (value != actorLead)
                {
                    actorLead = value;
                    OnPropertyChanged("ActorLead");
                }
            }
        }

        private string actressLead;
        public string ActressLead
        {
            get { return actressLead; }
            set
            {
                if (value != actressLead)
                {
                    actressLead = value;
                    OnPropertyChanged("ActressLead");
                }
            }
        }

        private string director;
        public string Director
        {
            get { return director; }
            set
            {
                if (value != director)
                {
                    director = value;
                    OnPropertyChanged("Director");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        private void OnPropertyChanged(string prop)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        public Movie(string title, int year, Uri image, string actorLead, string actressLead, string director)
        {
            Title = title;
            Year = year;
            Image = image;
            ActorLead = actorLead;
            ActressLead = actressLead;
            Director = director;
        }
    }