Simple small INotifyPropertyChanged implementation

后端 未结 6 1497
深忆病人
深忆病人 2021-01-06 07:51

Say I have the following class:

public MainFormViewModel
{
    public String StatusText {get; set;}
}

What is the easiest smallest way to g

相关标签:
6条回答
  • 2021-01-06 08:06

    I have a base class called "Model". It exposes a protected object called DataPoints, which is essentially a dictionary.

    C#

    public String StatusText {
        get { 
            return (string)DataPoints["StatusText"]; 
        } 
        set { 
            DataPoints["StatusText"] = value; 
        }
    }
    

    VB

    public Property StatusText as String 
        get 
            return DataPoints!StatusText 
        end get 
        set
            DataPoints!StatusText = value
        end set
    end property
    

    When you set a value in the DataPoints dictionary it does the following:

    1. Checks to make sure the value actually changed.
    2. Saves the new value
    3. Sets the IsDirty property to true.
    4. Raises the Property Changed event for the named property as well as the IsDirty and IsValid properties.

    Since it is a dictionary, it also makes loading objects from a database or XML file really easy.

    Now you may think reading and writing to dictionary is expensive, but I've been doing a lot of performance testing and I haven't found any noticable impact from this in my WPF applications.

    0 讨论(0)
  • 2021-01-06 08:07

    By leveraging EqualityComparer.Default you can reduce the property setter code down to one line as follows:

    private int unitsInStock;
    public int UnitsInStock 
    {
      get { return unitsInStock; }
      set { SetProperty(ref unitsInStock, value, "UnitsInStock"); }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void SetProperty<T>(ref T field, T value, string name) 
    {
      if (!EqualityComparer<T>.Default.Equals(field, value)) 
      {
        field = value;
        var handler = PropertyChanged;
        if (handler != null) 
        {
          handler(this, new PropertyChangedEventArgs(name));
        }
      }
    }
    

    If your view models inherit from a base class that defines the SetProperty method and the PropertyChanged event, then the amount of code required to support INotifyPropertyChanged in your child view models becomes very minimal (1 line).

    This approach is more verbose then the code weaving methods mentioned in other answers, but doesn't require you to modify your build process to accomplish it.

    Be sure to take a look at the upcoming C# 5 Caller Info attributes as well as it looks like they will allow us to avoid using a magic string in the method without the performance cost of reflection.

    UPDATE (March 1st, 2012):

    The .NET 4.5 Beta is out, and with it, you can further refine the above code to this which removes the need for the string literal in the caller:

    private int unitsInStock;
    public int UnitsInStock
    {
        get { return unitsInStock; }
        set 
        { 
            SetProperty(ref unitsInStock, value);
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            var handler = PropertyChanged;
            if (handler != null)
            {
              handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
    

    I have a blog post that talks about it in slightly more detail.

    0 讨论(0)
  • 2021-01-06 08:10

    Have a go of this http://code.google.com/p/notifypropertyweaver/

    All you need to do is implement INotifyPropertyChanged

    So your code will look like

    public MainFormViewModel : INotifyPropertyChanged
    {
        public String StatusText {get; set;}
    
        #region INotifyPropertyChanged Implementation
    }
    

    The build task will compile this (you never see the below code)

    public MainFormViewModel : INotifyPropertyChanged
    {
        public String StatusText {get; set;}
        private string statusText;
    
        public string StatusText 
        {
           get { return statusText; }
           set
           {
               if (value!= statusText)
               {
                   statusText = value;
                   OnPropertyChanged("StatusText");
               }
           }
        }
    
        #region INotifyPropertyChanged Implementation
    }
    
    0 讨论(0)
  • 2021-01-06 08:13

    Ive always liked this method

    private string m_myString;
    public string MyString
    {
        get { return m_myString; }
        set 
        {
            if (m_myString != value)
            {
                 m_myString = value;
                 NotifyPropertyChanged("MyString");
            }
        }
    }
    
    
    private void NotifyPropertyChanged(string property)
    {
        if (PropertyChanged != null)
             PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
    

    or for less code bloat

    set 
    {
        m_myString = value;
        NotifyPropertyChanged("MyString");
    }
    
    0 讨论(0)
  • 2021-01-06 08:24

    Unfortunately C# doesn't offer an easy mechanism to do that automatically... It has been suggested to create a new syntax like this :

    public observable int Foo { get; set; }
    

    But I doubt it will ever be included in the language...

    A possible solution would to use an AOP framework like Postsharp, that way you just need to decorate your properties with an attribute:

    public MainFormViewModel : INotifyPropertyChanged
    {
        [NotifyPropertyChanged]
        public String StatusText {get; set;}
    }
    

    (haven't tried, but I'm pretty sure Postsharp allows you to do that kind of thing...)


    UPDATE: OK, I managed to make it work. Note that it's a very crude implementation, using reflection on a private field to retrieve the delegate... It could certainly be improved, but I'll leave it to you ;)

    [Serializable]
    public class NotifyPropertyChangedAttribute : LocationInterceptionAspect
    {
        public override void OnSetValue(LocationInterceptionArgs args)
        {
            object oldValue = args.GetCurrentValue();
            object newValue = args.Value;
            base.OnSetValue(args);
            if (args.Instance is INotifyPropertyChanged)
            {
                if (!Equals(oldValue, newValue))
                {
                    RaisePropertyChanged(args.Instance, args.LocationName);
                }
            }
        }
    
        private void RaisePropertyChanged(object instance, string propertyName)
        {
            PropertyChangedEventHandler handler = GetPropertyChangedHandler(instance);
            if (handler != null)
                handler(instance, new PropertyChangedEventArgs(propertyName));
        }
    
        private PropertyChangedEventHandler GetPropertyChangedHandler(object instance)
        {
            Type type = instance.GetType().GetEvent("PropertyChanged").DeclaringType;
            FieldInfo propertyChanged = type.GetField("PropertyChanged",
                                                      BindingFlags.Instance | BindingFlags.NonPublic);
            if (propertyChanged != null)
                return propertyChanged.GetValue(instance) as PropertyChangedEventHandler;
    
            return null;
        }
    }
    

    Note that your class still need to implement the INotifyPropertyChanged interface. You just don't have to explicitly raise the event in your property setters.

    0 讨论(0)
  • 2021-01-06 08:24

    The PropertyChanged.Fody NuGet package does this.

    https://github.com/Fody/PropertyChanged

    • Add the PropertyChanged.Fody package to your project.
    • Reference PropertyChanged in your model: using PropertyChanged;
    • Add the [ImplementPropertyChanged] attribute to your class.

    All of the properties in the class will now magically implement INotifyPropertyChanged. Note - Fody works by modifying the emitted IL so you will never actually see the code in VS - it just magically does it.

    Additional docs: https://github.com/Fody/PropertyChanged/wiki/Attributes

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