Implementing INotifyPropertyChanged - does a better way exist?

前端 未结 30 2472
感情败类
感情败类 2020-11-21 05:23

Microsoft should have implemented something snappy for INotifyPropertyChanged, like in the automatic properties, just specify {get; set; notify;} I

相关标签:
30条回答
  • 2020-11-21 05:34

    I really like Marc's solution, but I think it can be slightly improved to avoid using a "magic string" (which doesn't support refactoring). Instead of using the property name as a string, it's easy to make it a lambda expression :

    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, () => Name); }
    }
    

    Just add the following methods to Marc's code, it will do the trick :

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        OnPropertyChanged(body.Member.Name);
    }
    
    protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(selectorExpression);
        return true;
    }
    

    BTW, this was inspired by this blog post updated URL

    0 讨论(0)
  • 2020-11-21 05:34

    Another combined solution is using StackFrame:

    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void Set<T>(ref T field, T value)
        {
            MethodBase method = new StackFrame(1).GetMethod();
            field = value;
            Raise(method.Name.Substring(4));
        }
    
        protected void Raise(string propertyName)
        {
            var temp = PropertyChanged;
            if (temp != null)
            {
                temp(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    Usage:

    public class TempVM : BaseViewModel
    {
        private int _intP;
        public int IntP
        {
            get { return _intP; }
            set { Set<int>(ref _intP, value); }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:36

    An idea using reflection:

    class ViewModelBase : INotifyPropertyChanged {
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        bool Notify<T>(MethodBase mb, ref T oldValue, T newValue) {
    
            // Get Name of Property
            string name = mb.Name.Substring(4);
    
            // Detect Change
            bool changed = EqualityComparer<T>.Default.Equals(oldValue, newValue);
    
            // Return if no change
            if (!changed) return false;
    
            // Update value
            oldValue = newValue;
    
            // Raise Event
            if (PropertyChanged != null) {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }//if
    
            // Notify caller of change
            return true;
    
        }//method
    
        string name;
    
        public string Name {
            get { return name; }
            set {
                Notify(MethodInfo.GetCurrentMethod(), ref this.name, value);
            }
        }//method
    
    }//class
    
    0 讨论(0)
  • 2020-11-21 05:40

    I haven't actually had a chance to try this myself yet, but next time I'm setting up a project with a big requirement for INotifyPropertyChanged I'm intending on writing a Postsharp attribute that will inject the code at compile time. Something like:

    [NotifiesChange]
    public string FirstName { get; set; }
    

    Will become:

    private string _firstName;
    
    public string FirstName
    {
       get { return _firstname; }
       set
       {
          if (_firstname != value)
          {
              _firstname = value;
              OnPropertyChanged("FirstName")
          }
       }
    }
    

    I'm not sure if this will work in practice and I need to sit down and try it out, but I don't see why not. I may need to make it accept some parameters for situations where more than one OnPropertyChanged needs to be triggered (if, for example, I had a FullName property in the class above)

    Currently I'm using a custom template in Resharper, but even with that I'm getting fed up of all my properties being so long.


    Ah, a quick Google search (which I should have done before I wrote this) shows that at least one person has done something like this before here. Not exactly what I had in mind, but close enough to show that the theory is good.

    0 讨论(0)
  • 2020-11-21 05:40

    If you are using dynamics in .NET 4.5 you don't need to worry about INotifyPropertyChanged.

    dynamic obj = new ExpandoObject();
    obj.Name = "John";
    

    if Name is bound to some control it just works fine.

    0 讨论(0)
  • 2020-11-21 05:40

    I have just found ActiveSharp - Automatic INotifyPropertyChanged, I have yet to use it, but it looks good.

    To quote from it's web site...


    Send property change notifications without specifying property name as a string.

    Instead, write properties like this:

    public int Foo
    {
        get { return _foo; }
        set { SetValue(ref _foo, value); }  // <-- no property name here
    }
    

    Note that there is no need to include the name of the property as a string. ActiveSharp reliably and correctly figures that out for itself. It works based on the fact that your property implementation passes the backing field (_foo) by ref. (ActiveSharp uses that "by ref" call to identify which backing field was passed, and from the field it identifies the property).

    0 讨论(0)
提交回复
热议问题