How to raise PropertyChanged event without using string name

后端 未结 8 1290
后悔当初
后悔当初 2020-11-29 00:22

It would be good to have ability to raise \'PropertyChanged\' event without explicit specifying the name of changed property. I would like to do something like this:

相关标签:
8条回答
  • 2020-11-29 01:10

    In the following example you have to pass 3 values (backing field, new value, property as lambda) but there are no magic strings and property changed event is only raised when it truly isn't equal.

    class Sample : INotifyPropertyChanged
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set { this.SetProperty(ref _name, value, () => this.Name); }
        }
    
    
        protected void SetProperty<T>(ref T backingField, T newValue, Expression<Func<T>> propertyExpression)
        {
            if (backingField == null && newValue == null)
            {
                return;
            }
    
            if (backingField == null || !backingField.Equals(newValue))
            {
                backingField = newValue;
                this.OnPropertyChanged(propertyExpression);
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyExpression.GetPropertyName()));
            }
        }
    
    }
    

    And the following code contains extension methods to get a property name from a lambda expression.

    public static class Extensions
    {
        public static string GetPropertyName<TProperty>(this Expression<Func<TProperty>> propertyExpression)
        {
            return propertyExpression.Body.GetMemberExpression().GetPropertyName();
        }
    
        public static string GetPropertyName(this MemberExpression memberExpression)
        {
            if (memberExpression == null)
            {
                return null;
            }
    
            if (memberExpression.Member.MemberType != MemberTypes.Property)
            {
                return null;
            }
    
            var child = memberExpression.Member.Name;
            var parent = GetPropertyName(memberExpression.Expression.GetMemberExpression());
    
            if (parent == null)
            {
                return child;
            }
            else
            {
                return parent + "." + child;
            }
        }
    
        public static MemberExpression GetMemberExpression(this Expression expression)
        {
            var memberExpression = expression as MemberExpression;
    
            if (memberExpression != null)
            {
                return memberExpression;
            }
    
            var unaryExpression = expression as UnaryExpression;
    
    
            if (unaryExpression != null)
            {
                memberExpression = (MemberExpression)unaryExpression.Operand;
    
                if (memberExpression != null)
                {
                    return memberExpression;
                }
    
            }
            return null;
        }
    
        public static void ShouldEqual<T>(this T actual, T expected, string name)
        {
            if (!Object.Equals(actual, expected))
            {
                throw new Exception(String.Format("{0}: Expected <{1}> Actual <{2}>.", name, expected, actual));
            }
        }
    
    }
    

    Finally some test code:

    class q3191536
    {
        public static void Test()
        {
            var sample = new Sample();
            var propertyChanged = 0;
    
            sample.PropertyChanged += 
                new PropertyChangedEventHandler((sender, e) => 
                    {
                        if (e.PropertyName == "Name")
                        {
                            propertyChanged += 1;
                        }
                    }
                );
    
            sample.Name = "Budda";
    
            sample.Name.ShouldEqual("Budda", "sample.Name");
            propertyChanged.ShouldEqual(1, "propertyChanged");
    
            sample.Name = "Tim";
            sample.Name.ShouldEqual("Tim", sample.Name);
            propertyChanged.ShouldEqual(2, "propertyChanged");
    
            sample.Name = "Tim";
            sample.Name.ShouldEqual("Tim", sample.Name);
            propertyChanged.ShouldEqual(2, "propertyChanged");
        }
    }
    
    0 讨论(0)
  • 2020-11-29 01:11

    I´m using the extension method

    public static class ExpressionExtensions {
        public static string PropertyName<TProperty>(this Expression<Func<TProperty>> projection) {
            var memberExpression = (MemberExpression)projection.Body;
    
            return memberExpression.Member.Name;
        }
    }
    

    in combination with the following method. The method is defined in the class that implements the INotifyPropertyChanged interface (Normally a base class from which my other classes are derived).

    protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection) {
        var e = new PropertyChangedEventArgs(projection.PropertyName());
    
        OnPropertyChanged(e);
    }
    

    Then i can raise the PropertyChanged-Event as follows

    private double _rate;
    public double Rate {
            get {
                return _rate;
            }
            set {
                if (_rate != value) {
                  _rate = value;                     
                  OnPropertyChanged(() => Rate );
                }
            }
        }
    

    Using this approach, its easy to rename Properties (in Visual Studio), cause it ensures that the corresponding PropertyChanged call is updated too.

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