There are times when I think my friends in the C# team are just listening to me gripe before adding a cool new feature that solves the problem. (I have never quite outgrown the assumption that the world revolves around me).
A couple years ago I grew really tired of having to create the same old same old when it came to public properties,
private int _myValue; public int MyValue { get { return _myValue; } set { _myValue = value; } }
No sooner did I whine about this, then along came automatic properties and life became much easier. The above boiled down to
private int MyValue { get; set; }
My new gripe (and yours, no doubt) was the work involved in sending in the name of the calling property for INotifyPropertyChanged. It became common practice to write a helper method that took the name of the property and fed it to the PropertyChanged event, but it was a string being passed around and one tiny typo and your property did not update the UI. Here’s the old way…
private string _name; public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } } private void RaisePropertyChanged( string caller) { if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs( caller ) ); } }
Notice that the Name property has to send “Name” to the RaisePropertyChanged helper method.
With C# 5, this problem is solved (Hoot!) by adding an attribute to the argument in RaisePropertyChanged: [CallerMemberName] The new syntax, which is much less error prone is:
private string _name; public string Name { get { return _name; } set { _name = value; RaisePropertyChanged(); } } private void RaisePropertyChanged( [CallerMemberName] string caller = "" ) { if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs( caller ) ); } }
Notice that you no longer include the name of the property when calling RaisePropertyChanged. You just call it, and due to the attribute, the called method figures out what the caller member name is and places that into the string parameter. Sweet.
Here’s the complete C# for the Employee class for Win 8, followed by the XAML to test it on Win 8…
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; namespace PropertyChanged { class Employee : INotifyPropertyChanged { private string _name; public string Name { get { return _name; } set { _name = value; RaisePropertyChanged(); } } public void SetName( string newName ) { Name = newName; } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged( [CallerMemberName] string caller = "") { if ( PropertyChanged != null ) { PropertyChanged( this, new PropertyChangedEventArgs( caller ) ); } } } }
The XAML…
<Page x:Class="PropertyChanged.MainPage" IsTabStop="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PropertyChanged" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <StackPanel Orientation="Horizontal"> <TextBlock Name="NameDisplay" Text="{Binding Name}" /> <Button Height="100" Width="200" Content="Change name" Name="ChangeName" Click="ChangeName_Click_1" /> </StackPanel> </Grid> </Page>
The code-behind…
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; namespace PropertyChanged { public sealed partial class MainPage : Page { Employee emp = new Employee(); public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { DataContext = emp; emp.SetName( "Joe" ); } private void ChangeName_Click_1( object sender, RoutedEventArgs e ) { emp.SetName( "Bob" ); } } }
77 Responses to C# 5–Making INotifyPropertyChanged Easier