Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Using CallerMemberName for property change notification in XAML apps

Pete Brown - 25 February 2013

.NET 4.5 quietly introduced several attributes which are useful for debugging and error reporting: CallerMemberName, CallerFilePath and CallerLineNumber, all collectively referred to as "Caller Information". One of those, CallerMemberName, is also very useful for MVVM apps and other apps using INotifyPropertyChanged for change notifications.

Getting the calling function name

The CallerMemberName attribute, from the System.Runtime.CompilerServices namespace, supplies the name of the function which called the function with the attribute in its parameter list. For example, if you have a function defined like this:

private void DoSomething([CallerMemberName] string callingFunction = null)
{
if (callingFunction != null)
Debug.WriteLine("Calling function name is " + callingFunction);
else
Debug.WriteLine("Calling function not supplied.");
}

Then, you call it like this:

private void CallSomething()
{
DoSomething();
}

The callingFunction parameter will be filled with the name of the calling function. In this case, the output indicate that the calling function is "CallSomething". This works because the property uses the CallerMemberName attribute and has a default value. The default value is a required part of this pattern.

Using CallerMemberName in property change notification

XAML uses an interface and event based property change notification pattern to alert the binding system when a non-static property has been changed. (WPF supports binding to static properties, and although it uses the event, it does not use the interface as there's no class instance.) The interface used is INotifyPropertyChanged, and regardless of how you feel about the requirement to use this interface for change notification, it seems it is here to stay.

The problem

One real issue with INotifyPropertyChanged is the requirement to pass in the name of the calling property as a string. Some time ago, I spoke with the people who originally designed this approach, and despite me not caring much for it, I'm convinced that it was, in fact, the correct approach to use. It provides the best performance and flexibility compared to other approaches, and required no changes to the language specs or the CLR.

Code using this approach, without the benefit of any MVVM toolkits or other base classes, typically looked something like this:

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

// ...

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

This leads to countless problems when properties are refactored and renamed, but the string (which is not verified by the compiler) is not changed. For example, here' I've renamed the Title property to Name. This will compile just fine:

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

// ...

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

Not only will this compile just fine, but it will run as well. The only real indication of a problem will be the field not updating in the UI when changed from someplace else in the code, or another part of the UI. This can be really easy to miss in testing.

The solution

There are multiple ways to solve this, but the newest, and perhaps most elegant, is an approach using the CallerMemberName attribute. This approach is used by the default Windows Store XAML app templates in the BindableBase class:

public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;

storage = value;
this.OnPropertyChanged(propertyName);
return true;
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

If you were passing in strings for property names before, this base class will simplify your code and also save you from the difficult-to-track binding bugs you'd get if you changed property names without updating the string in the property change notification call. Now, your setters can be as simple as this:

public class PuzzleLevel : BindableBase
{
private string _title;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}

private int _highScore;
public int HighScore
{
get { return _highScore; }
set { SetProperty(ref _highScore, value); }
}

private bool _isLocked;
public bool IsLocked
{
get { return _isLocked; }
set { SetProperty(ref _isLocked, value); }
}
}

There were other ways to do this, but they all involved more steps and additional parameters. The use of third-party MVVM toolkits and lambda expressions simplified that somewhat. Those will still work and work well, but for new code, I recommend you consider using the built-in CallerMemberName attribute approach.

The CallerMemberName (and other Caller* functions) are changed into literal values at compile time, so there's no runtime reflection lookup or similar performance hit like that encountered by other methods. This, combined with its ease of use, makes it a no-brainer to use.

This approach also works with VB with appropriate syntax changes.

       
posted by Pete Brown on Monday, February 25, 2013
filed under:        

8 comments for “Using CallerMemberName for property change notification in XAML apps”

  1. Scott Slacksays:
    To make my code more terse, I do this:
    using CMN = System.Runtime.CompilerServices.CallerMemberNameAttribute;
    using CFP = System.Runtime.CompilerServices.CallerFilePathAttribute;
    using CLN = System.Runtime.CompilerServices.CallerLineNumberAttribute;
    Then a do-nothing delegate in the event-handler list eliminates the check for a null Event Handler.

    These attributes can help fill out an Exception also.
    public class CallingException : Exception
    {
    public string SourceFile { get; private set; }
    public int Line { get; private set; }
    public CallingException(string msg, string sourceFilePath,int sourceLineNumber) : base(msg)
    {
    this.SourceFile = sourceFilePath;
    this.Line = sourceLineNumber;
    }
    }


    public static void ThrowException(string message, [CFP] string sourceFilePath = "",
    [CLN] int sourceLineNumber = 0)
    {
    throw new CallingException(message, sourceFilePath, sourceLineNumber);
    }


    Call it: Utilities.ThrowException("Holy Cow!"); // exception knows the file and line of this throw.
  2. Petesays:
    Thanks, guys.

    @Robert
    Yeah, sorry, man. XP is older than my mortgage, my marriage, my kids, some of my "vintage" synth gear, and almost every computer I own, so there's not much I can do there, unfortunately.

    Mainstream support ended a few years back. Hopefully, with extended support for SP3 ending in another year and a half or so, we can finally put XP-constrained development out to pasture. Just consider that Windows 98 is only 3 years older than XP. :)

    @Scott
    Nice.


    Also, I should point out that Simon Cropp reminded me of this approach, which also happens at compile time (so same performance characteristics)
    https://github.com/Fody/PropertyChanged

    Pete
  3. Simonsays:
    @Pete continued form twitter...

    You are correct they have the same runtime performance

    I guess I prefer the succinctness of this

    public class PuzzleLevel : INotifyPropertyChanged
    {
    public event PropertyChangedEventHandler PropertyChanged;
    public string Title;
    public int HighScore
    public bool IsLocked
    }

    When i combine this https://github.com/Fody/PropertyChanged with this https://github.com/Fody/Fielder

    -no base class required
    -no get or set required
    -automatic equality checking
    -handles dependent properties

    And much more readable.

    Disclaimer: these are my projects. I am not trying to push them just show a different approach
  4. Petesays:
    Thanks Simon

    All good points about your stuff. I encourage folks to check it out and see if they like the approach for their own projects.

    The BindableBase class I show above is included in the Visual Studio template for Windows Store apps. Programmers can change from object.Equals to EqualityComparer<T> if they want.

    Pete

Comment on this Post

Remember me