How to implement INotifyPropertyChanged in C# 6.0?

后端 未结 3 786
半阙折子戏
半阙折子戏 2020-11-29 08:36

The answer to this question has been edited to say that in C# 6.0, INotifyPropertyChanged can be implemented with the following OnPropertyChanged procedure:

         


        
相关标签:
3条回答
  • 2020-11-29 08:56

    After incorporating the various changes, the code will look like this. I've highlighted with comments the parts that changed and how each one helps

    public class Data : INotifyPropertyChanged
    { 
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            //C# 6 null-safe operator. No need to check for event listeners
            //If there are no listeners, this will be a noop
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        // C# 5 - CallMemberName means we don't need to pass the property's name
        protected bool SetField<T>(ref T field, T value,
        [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) 
                return false;
            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }
    
        private string name;
        public string Name
        {
            get { return name; }
            //C# 5 no need to pass the property name anymore
            set { SetField(ref name, value); }
        }
    }
    
    0 讨论(0)
  • 2020-11-29 08:56

    I know this question is old, but here is my implementation

    Bindable uses a dictionary as a property store. It's easy enough to add the necessary overloads for a subclass to manage its own backing field using ref parameters.

    • No magic string
    • No reflection
    • Can be improved to suppress the default dictionary lookup

    The code:

        public class Bindable : INotifyPropertyChanged
        {
            private Dictionary<string, object> _properties = new Dictionary<string, object>();
    
            /// <summary>
            /// Gets the value of a property
            /// <typeparam name="T"></typeparam>
            /// <param name="name"></param>
            /// <returns></returns>
            protected T Get<T>([CallerMemberName] string name = null)
            {
                object value = null;
                if (_properties.TryGetValue(name, out value))
                    return value == null ? default(T) : (T)value;
                return default(T);
            }
    
            /// <summary>
            /// Sets the value of a property
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="value"></param>
            /// <param name="name"></param>
            protected void Set<T>(T value, [CallerMemberName] string name = null)
            {
                if (Equals(value, Get<T>(name)))
                    return;
                _properties[name] = value;
                OnPropertyChanged(name);
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    

    used like this

    public class Item : Bindable
    {
         public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
    }
    
    0 讨论(0)
  • 2020-11-29 09:00

    I use the same logic in my project. I have a base class for all view models in my app:

    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    
    public class PropertyChangedBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    Every view model inherits from this class. Now, in the setter of each property I just need to call OnPropertyChanged().

    public class EveryViewModel : PropertyChangedBase
    {
        private bool initialized;
        public bool Initialized
        {
            get
            {
                return initialized;
            }
            set
            {
                if (initialized != value)
                {
                    initialized = value;
                    OnPropertyChanged();
                }
            }
        }
    

    Why does it work?

    [CallerMemberName] is automatically populated by the compiler with the name of the member who calls this function. When we call OnPropertyChanged from Initialized, the compiler puts nameof(Initialized) as the parameter to OnPropertyChanged

    Another important detail to keep in mind

    The framework requires that PropertyChanged and all properties that you're binding to are public.

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