5

I have a class with tens of properties that need to raise property changed events, currently my code looks something like

public class Ethernet : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string timeStamp;

    public string TimeStamp
    {
        get { return timeStamp; }
        set
        {
            timeStamp = value;

            if(PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("TimeStamp"));
        }
    }
}

Is there a shorter way in C# to write this sort of code, I am doing excessive copy/paste operations for each property and I feel there must be a better way.

JME
  • 2,293
  • 9
  • 36
  • 56
  • Not really, but you can use tools like Fody (free) out PostSharp (paid) to make it easier. – Ron Beyer Nov 10 '15 at 03:04
  • 2
    Possible duplicate of [Implementing INotifyPropertyChanged - does a better way exist?](http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist) – drf Nov 10 '15 at 03:56

4 Answers4

7

The quoted code is not thread safe as written. See Pattern for implementing INotifyPropertyChanged? why the code below is better, and the link to Eric Lippert's blog in the accepted reply why the story doesn't end there.

    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null) handler(this, new PropertyChangedEventArgs("TimeStamp"));

For answers to the actual question, see Implementing INotifyPropertyChanged - does a better way exist? including this C# 6.0 shortcut.

    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TimeStamp"));
StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
dxiv
  • 16,984
  • 2
  • 27
  • 49
  • When `.?` returns null you will have code line like this `null;`. This is not possible so you will end up compile time error. (but upvoted for other information.) – M.kazem Akhgary Nov 10 '15 at 03:44
  • 2
    @M.kazemAkhgary - No, the new c# 6.0 `.?` is a null-check code. It only calls the `Invoke` when the value on the left of the `.?` is not `null`. It compiles (in c# 6.0) just fine. – Enigmativity Nov 10 '15 at 03:53
  • @M.kazem my understanding is that with .? null there is no invocation and no error. Besides being mentioned in the linked stackoverflow answer, this is also the recommended way to write it in Essential C# 6.0 by Mark Michaelis & Eric Lippert ("advanced topic: leveraging the null-conditional operator with delegates"). – dxiv Nov 10 '15 at 03:57
  • 6
    If you are using C# 6, it would be the best if you use `nameof(TimeStamp)` instead of `TimeStamp`. If you rename the property later, it won't compile if this doesn't get changed. – Snicker Nov 10 '15 at 20:47
  • @Snicker that's good advice, indeed. But since I didn't post complete code, I chose to keep the snippets as close to the original as possible. – dxiv Nov 12 '15 at 18:59
1

Do take a look at this answer: https://stackoverflow.com/a/2339904/259769

My code in provides and extension method to replace much of the setting code and let's you shorten your code to this:

public class Ethernet : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string timeStamp;

    public string TimeStamp
    {
        get { return timeStamp; }
        set { this.NotifySetProperty(ref timeStamp, value, () => this.TimeStamp); }
    }
}

The other distinct advantage with this code is that it immediately becomes strongly-typed against the name of the property.

Community
  • 1
  • 1
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

In MVVM pattern, properties changed/ing are used often and a typical basic solution is as follows:

  public class ViewModelBase : INotifyPropertyChanged
  {
     public event PropertyChangedEventHandler PropertyChanged;

     protected void FirePropertyChanged([CallerMemberName] string propertyName = null)
     {
        if (propertyName == null)
           throw new ArgumentNullException("propertyName");
        try
        {
           this.OnPropertyChanged(propertyName);
        }
        catch (Exception exception)
        {
           Trace.TraceError("{0}.OnPropertyChanged threw {1}: {2}", this.GetType().FullName, exception.GetType().FullName, exception);
        }
     }
     protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
     {
        var handler = PropertyChanged;
        if (handler != null)
        {
           handler(this, new PropertyChangedEventArgs(propertyName));
        }
     }
  }

 public class Ethernet : ViewModelBase
  {
     private DataTime timeStamp;

     public DateTime TimeStamp
     {
        get
        {
           return timeStamp;
        }
        set
        {
           timeStamp = value;
           FirePropertyChanged();
        }
     }
  }
Ahmad
  • 1,462
  • 15
  • 23
0

I'm in love with this class:

[Serializable]
public class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual bool SetProperty<T>(T value, ref T field, Expression<Func<object>> property)
    {
        return SetProperty(value, ref field, GetPropertyName(property));
    }

    protected virtual bool SetProperty<T>(T value, ref T field, [CallerMemberName] string propertyName = null)
    {
        if (field == null || !field.Equals(value))
        {
            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }
        return false;
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public void OnPropertyChanged(Expression<Func<object>> property)
    {
        OnPropertyChanged(GetPropertyName(property));
    }

    protected string GetPropertyName(Expression<Func<object>> property)
    {
        var lambda = property as LambdaExpression;
        MemberExpression memberExpression;

        var unaryExpression = lambda.Body as UnaryExpression;
        if (unaryExpression != null)
        {
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = (MemberExpression) lambda.Body;
        }

        var propertyInfo = memberExpression?.Member as PropertyInfo;
        return propertyInfo?.Name ?? string.Empty;
    }
}

A huge advantage of this is that it checks if the value changed. This will minimize the update calls to the view. For your example, it can look like that:

public class Ethernet : PropertyChangedBase
{
    private string _timeStamp;

    public string TimeStamp
    {
        get { return _timeStamp; }
        set { SetProperty(value, ref _timeStamp); }
    }
}

If you want it really comfortable, you can write a code snippet for that. This would be the snippet part:

    <Snippet>
      <Declarations>
        <Literal>
          <ID>PropertyName</ID>
          <Type>String</Type>
          <ToolTip>The property name</ToolTip>
          <Default>NewProperty</Default>
        </Literal>
        <Literal>
          <ID>PropertyType</ID>
          <Type>
          </Type>
          <ToolTip>Replace with the type of the property</ToolTip>
          <Default>string</Default>
        </Literal>
        <Object>
          <ID>PrivateVariable</ID>
          <Type>Object</Type>
          <ToolTip>The name of the private variable</ToolTip>
          <Default>newPropertyValue</Default>
        </Object>
      </Declarations>
      <Code Language="csharp" Kind="method decl"><![CDATA[        private $PropertyType$ _$PrivateVariable$;
        public $PropertyType$ $PropertyName$
        {
            get { return _$PrivateVariable$; }
            set
            {
                SetProperty(value, ref _$PrivateVariable$);
            }
        }]]></Code>
    </Snippet>
Snicker
  • 957
  • 10
  • 16