SoFunction
Updated on 2025-04-06

Simplify the implementation of InotifyPropertyChanged using CallerMemberName in WPF

In WPF, when we want to bind the properties of a normal object using MVVM, we often need to get notifications of property changes on the interface.

    class NotifyObject : INotifyPropertyChanged
    {
        private int number;
        public int Number
        {
            get { return number; }
            set { number = value; OnPropertyChanged("Number"); }
        }

        private string text;
        public string Text
        {
            get { return text; }
            set { text = value; OnPropertyChanged("Text"); }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName = "")
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

There is a big hidden danger in doing this, that is, the attribute name is passed in hard-coded form of strings. Once the spelling is wrong or the refactoring code forgets to update the string, it will cause the interface to not be updated. (The hard-coded method itself is unreliable behavior)

Although this is not a problem in itself, it is not a good practice. Some people also use some means to solve this problem, some through expression trees, and some through Attribute injection.

In terms of performance, injection is a better way, but it often introduces relatively complex frameworks. In fact, aCaller informationThe syntax is convenient for us to obtain the caller's function name and location, and it can solve the above problem very quickly and quickly:

    class NotifyObject : INotifyPropertyChanged
    {
        private int number;
        public int Number
        {
            get { return number; }
            set { number = value; OnPropertyChanged(); }
        }

        private string text;
        public string Text
        {
            get { return text; }
            set { text = value; OnPropertyChanged(); }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

The code is very simple, so I won't introduce it here.

Going further

Sometimes, in order to reduce the frequency of notifications, notifications are often written in the following form:

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

            number = value;
            OnPropertyChanged();
        }
    }

    private string text;
    public string Text
    {
        get { return text; }
        set
        {
            if (text == value)
                return;

            text = value;
            OnPropertyChanged();
        }
    }

This writing method is very monotonous, and when there are many attributes, the code becomes very cumbersome. Here I wrote a general point function to unify them, and I can use it directly next time.

    private int number;
    public int Number
    {
        get { return number; }
        set { UpdateProper(ref number, value); }
    }

    private string text;
    public string Text
    {
        get { return text; }
        set { UpdateProper(ref text, value); }
    }

    protected void UpdateProper<T>(ref T properValue, T newValue, [CallerMemberName] string properName = "")
    {
        if ((properValue, newValue))
            return;

        properValue = newValue;
        OnPropertyChanged(properName);
    }

Due to the syntax limitation of C#, event cannot be called outside the class, so it cannot be written as an extension method. Here I will simply write it as an object, and I will directly modify it next time:

    class NotifyObject : INotifyPropertyChanged
    {
        private int number;
        public int Number
        {
            get { return number; }
            set { UpdateProper(ref number, value); }
        }

        private string text;
        public string Text
        {
            get { return text; }
            set { UpdateProper(ref text, value); }
        }

        protected void UpdateProper<T>(ref T properValue, T newValue, [CallerMemberName] string properName = "")
        {
            if ((properValue, newValue))
                return;

            properValue = newValue;
            OnPropertyChanged(properName);
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

This is all about this article about using CallerMemberName to simplify InotifyPropertyChanged in WPF. I hope it will be helpful to everyone's learning and I hope everyone will support me more.