Implementing INotifyPropertyChanged - does a better way exist?
Asked Answered
E

36

759

Microsoft should have implemented something snappy for INotifyPropertyChanged, like in the automatic properties, just specify {get; set; notify;} I think it makes a lot of sense to do it. Or are there any complications to do it?

Can we ourselves implement something like 'notify' in our properties. Is there a graceful solution for implementing INotifyPropertyChanged in your class or the only way to do it is by raising the PropertyChanged event in each property.

If not can we write something to auto-generate the piece of code to raise PropertyChanged event?

Extrabold answered 22/8, 2009 at 9:34 Comment(10)
see #1329638 for a compiler checked way of implementing INotifyPropertyChanged. Avoiding having the property names as a magic string.Marlee
code.google.com/p/notifypropertyweaver may be of useMarlee
above link is dead. github.com/SimonCropp/NotifyPropertyWeaverWhitcomb
Despite having adequate alternatives, nothing for my team has been as painless as Postsharp's Domain Toolkit (will be bundled with normal Postsharp in upcoming v3.0 I think). [NotifyPropertyChanged] on the class, [NotifyPropertyChangedIgnore] on the props to ignore.Dulla
@Codest and now your link is deadFelisha
@Felisha github.com/Fody/PropertyChanged/wiki/…Whitcomb
I agree with the OP here. It's really sad that this doesn't exist in Roslyn yet. Further there needs to be a way to notate that one property is dependent on the value of another (ideally with attributes, and the nameof operator).Flatworm
At the time making changes to C# wasn't possible given we had a huge back log of inter-dependencies. So back when MVVM was born i guess, we just really didn't put to much effort into solving this issue and I know the Patterns & Practices team had a few goes at it along the way (hence you also got MEF as part of that research thread). Today i think [CallerMemberName] is the answer to the above.Tamarisk
I think @tom-gilder 's has a better answer. https://mcmap.net/q/54013/-implementing-inotifypropertychanged-does-a-better-way-existLuminance
ObservableCollection eliminates the need to implement OnPropertyChanged, for simple situations. See How to: Create and Bind to an ObservableCollection, and ObservableCollection.Pressure
D
753

Without using something like postsharp, the minimal version I use uses something like:

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

Each property is then just something like:

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

which isn't huge; it can also be used as a base-class if you want. The bool return from SetField tells you if it was a no-op, in case you want to apply other logic.


or even easier with C# 5:

protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
{...}

which can be called like this:

set { SetField(ref name, value); }

with which the compiler will add the "Name" automatically.


C# 6.0 makes the implementation easier:

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

...and now with C#7:

protected void OnPropertyChanged(string propertyName)
   => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

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 => name;
    set => SetField(ref name, value);
}

And, with C# 8 and Nullable reference types, it would look like this:

public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(propertyName);
    return true;
}

private string name;
public string Name
{
    get => name;
    set => SetField(ref name, value);
}
Darin answered 22/8, 2009 at 9:34 Comment(46)
Nice trick Marc ! I suggested an improvement to use a lambda expression instead of the property name, see my answerAuberon
DevXpress Xpo does it this way.Spermatogonium
@Thomas - the lambda is all well and good, but it adds a lot of overhead for something that is actually very simple. A handy trick, but I'm not sure it is always practical.Darin
@Marc - Yes, it can probably degrade performance... However I really like the fact that it's checked at compile time, and is correctly refactored by the "Rename" commandAuberon
@PKKG that means nobody has subscribed to the eventDarin
What about: private void RaisePropertyChanged( [CallerMemberName] string caller = "") { if ( PropertyChanged != null ) { PropertyChanged( this, new PropertyChangedEventArgs( caller ) ); } }Parasiticide
@ThomasLevesque The refactor ability supersedes the performance hit imo. Never pre-optimise where sacrificing maintainability is concerned.Numismatist
@Numismatist fortunately, with C#5 there is no need to compromise - you can get the best of both via (as Pedro77 notes) [CallerMemberName]Darin
@MarcGravell I wish I could know this joy. I'm still supporting windows XP and have to use .Net 4 :*(Numismatist
@Numismatist the language and framework are separate; you can use the C# 5 compiler, target .NET 4, and just add the missing attribute yourself - it will work fine. It just has to have the correct name and be in the correct namespace. It does not need to be in a specific assembly.Darin
In case reflection is not an Issue in , one can use MethodBase.GetCurrentMethod().Name.Substring(4); instead of writing the Property name every time. the 4 is to trim the "set_" string. I know, it is ugly...Strom
@Numismatist [CallerMemberName] is available for .Net 4.0 using the Microsoft.Bcl nuget package! nuget.org/packages/Microsoft.BclBuxtehude
Is it possible to write an intermediate parent class that exposes the PropertyChagned event and implements SetField<>? It seems like a code smell to have this method defined in every class that implements INotifyPropertyChanged. Likewise, can we not squeeze out the "notifying public property" idiom somehow?Spinose
@Jay there are ways of doing it with static methods so that you only need to declare it once, sureDarin
I just noticed BindableBase does what I was looking for. Thanks, @Marc.Spinose
What black magic is this? Can somebody point to some reference or explanation? What is [CallerMemberName] and PropertyChanged?.Invoke?Honeysweet
@FandiSusanto [CallerMemberName] is a parameter attribute that the C# 5 compiler recognizes, and automatically injects the name of the calling method/member (as a constant string, so interned, etc); The ?. syntax is C# 6 for null propagation; if the object on the left is non-null, the method will be invoked; if the object is null,it won't. If there was a return value implied, it would assume the value null if the object was null.Darin
@MarcGravell perhaps you could clean up the code for C# 6, so this can be avoided? I now regret answering instead of voting to closeRoane
Downvoted. This is not automatic. -- Even the recommended way in C# 5 is lame. C# 6 adds a whole slew of built-in ways to do cool things with properties -- but not this, and it's a shame.Flatworm
@Flatworm which bit is "not automatic"? There's actually something very interesting being discussed in C# 7 which may do what you want, but right now, without using additional tools (like postsharp), this is about the simplest you'll get.Darin
I think an even better variation in C# 6 would be to call PropertyChanged directly from the setter of an attribute: PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CallingAttribute)));Christy
Re: the C#6 example- It's occasionally useful to pass null as the argument to the PropertyChanged event (to indicate that all properties on the object have changed). Thus, it might still be valuable to utilize a separate SetField method just so that OnPropertyChanged can be explicitly called with null for this purpose.Albano
@MarcGravell I'd suggest an even shorter way. Yet another answer won't be read and it's in part had been suggested below, but, to summarize, I declare any property as follows: public string MyProp { get { return Get(); } set { Set(value); } }. The benefit is it's shorter (a one-liner if you wish) and it can be blindly copied without changing to make any property bindable. No backing fields also. The downside - having a base class. The trick is to make Get return dynamic and Set take object. And no, this does not slow down your app in any significant way. But it does speed up the dev.Kolk
What's the reasoning for your SetField method using EqualityComparer<T>.Default.Equals(field, value) rather than just field == value? Is this just to better support the case where type T has been implemented poorly?Gosse
@AdamGoodwin generics do not work with operators like ==Darin
Oops, I knew it would be something obvious – I should have just tried it.Gosse
is there a way to not write that boilerplate in every new class i create ??Yoakum
Furthermore in C# 6.0 you can use expression body notation: protected void OnPropertyChanged( [CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));Microbicide
And for C#7 we can skip the nameof(Name) param to make it a little shorter.Repentant
@AndreyKlochkov you're right - fixed; looks like that segment was added at a later date, but: all fixedDarin
Out of interest: why SetField and not SetProperty?Quacksalver
@Quacksalver because it takes, and assigns to, a reference to a field - not a reference to a property (there is no such thing as a reference to a property, unless you count ref return which is new)Darin
I've recently started to incorporate bellow check into the SetField method. I often copy and paste the code into a class and forget to inherit INotifyPropertyChanged. This gives me a clear reminder. if (this != null && !(this is INotifyPropertyChanged)) { throw new Exception("Forgot to inherit INotifyPropertyChanged"); }Wellhead
should the c# & code be: set => SetField(ref name, value, "name");Closehauled
@MarkRedman no; the whole point of [CallerMemberName] is that it provides the caller name automaticallyDarin
@MarcGravell, ok, makes sense, was sure I am am C#7, but indicated a missing parameter...Closehauled
@MarkRedman that's odd - just to be sure, I did test it here before I replied, and it compiled fineDarin
This could probably benefit from C#8's upcoming default interface implementations. That way a lot of the boilerplate could possibly be moved into the interface declaration.Isoagglutinin
This is pretty cool. However, in MVVM, the pattern is usually, in my experience, that the ViewModel wraps a model. So there is no backing field. Instead we need to set model.Name to a value. We can't pass model.Name in as a ref.Disburden
I found this absolutely great until I noticed that I do not have the new value on the event PropertyChanged. the field = value somehow does not happen / the field has not been updated in the class where i implemented this (and assigned an event where i want to work with the new value)Iliad
@Iliad that probably means that your IEquatable<T> implementation isn't right - see where we short-circuit if the two values are equal, at if (EqualityComparer<T>.Default.Equals(field, value)) - this uses IEquatable<T>, or regular Equals(object) if that isn't availableDarin
@MarcGravell thank you for the quick reply! I do not implement IEquatable myself so it defaults. I should clarify the issue: The two values are not equal. The line field = value; does happen but I can not retrieve that new value in the class I implemented the interface AND the function I put onto the event handler. Just after the field = value; the OnPropertyChanged will be called. I assign an event to that (in the class where the field lives) but the Field will not have the new value there. If this is not intended behavior I will start a new topic.Iliad
@Iliad I'd probably need to see it in context as a repro to understand exactly what your scenario isDarin
@MarcGravell It works perfect in another other class. I moved my code from on property changed between the brakets in the if(SetField(...,..)){//my property changed code} because ofc a test version should be here yesterday :) I will probably start anew topic if this becomes relevant for me againIliad
C#9 allows the new PropertyChangedEventArgs(propertyName) expression to be shortened to just new(propertyName).Prerogative
this is, to the T, pun intended, now an included refactoring in ReSharper.Ander
U
206

.Net 4.5 introduced Caller Information Attributes:

private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
     // make sure only to call this if the value actually changes

     var handler = PropertyChanged;
     if (handler != null) {
        handler(this, new PropertyChangedEventArgs(caller));
     }
}

It's probably a good idea to add a comparer to the function as well.

EqualityComparer<T>.Default.Equals

More examples here and here

Also see Caller Information (C# and Visual Basic)

Unhair answered 22/8, 2009 at 9:34 Comment(9)
Brilliant! But why is it generic?Spokeswoman
@Spokeswoman I guess it doesn't have to be, I was just playing with the idea of having the function set the property as well. I'll see if I can update my answer provide the full solution. The extra examples do a good job that in the meantime.Unhair
It was introduced by C # 5.0. It has nothing to do with .net 4.5, but this is a great solution!Deannadeanne
@J. Lennon .net 4.5 still has something to do with it, after all the attribute comes from somewhere msdn.microsoft.com/en-au/library/…Unhair
@Lavinski change your application to eg .NET 3.5 and see what will work (in vs2012)Deannadeanne
@Lavinski sorry, I was wrong. CallerMemberName was introduced by other implementations for older versions of .NET (async bridge and microsoft.bcl)Deannadeanne
@Lavinski It is possible to use this while targeting older framework versions, with a minor tweak: https://mcmap.net/q/55481/-is-the-callermembername-attribute-in-4-5-quot-able-to-be-faked-quotSpatial
A great approach, but it does have some shortcomings: awkwardcoder.blogspot.co.uk/2012/07/…Uppercase
Is there a neat way to use this when you just have public getters and are using private setters with a ChangeMyProperty(string value) type of method call to follow a more DDD pattern? I assume [CallerMemberName] will report incorrectly with this approach?Quindecennial
A
170

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.

Auberon answered 22/8, 2009 at 9:34 Comment(7)
There's at least one framework using this method, ReactiveUI.Embodiment
Very late, this meant going through reflection, which meant a performance hit. It could be acceptable, but setting a property isn't a place where I'd like my application to spend to many cycles.Deposal
@BrunoBrant Are you sure there is a performance hit? According to the blog post the reflection happens during compile time rather than runtime (i.e. static reflection).Electrotechnics
@NathanielElkins you are correct. I was remembering that I actually had a performance hit when using expressions, but the reason for that is that I needed to compile the expression afterwards. One more tip: this fails if the users points to a property on a sublevel, ie, "foo.bar.baz". You can expand the algorithm to cover that.Deposal
I believe your whole OnPropertyChanged<T> is obsolete with the nameof operator of C# 6, making this monster a bit sleeker.Karakorum
@Traubenfuchs, actually, C#5's CallerMemberName attribute makes it even simpler, since you don't need to pass anything at all ...Auberon
Though this has been since superseded by C# improvements it was a very cool approach!Concierge
M
144

There's also Fody which has a AddINotifyPropertyChangedInterface add-in, which lets you write this:

[AddINotifyPropertyChangedInterface]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }
}

...and at compile time injects property changed notifications.

Midships answered 22/8, 2009 at 9:34 Comment(6)
I think this is exactly what OP was looking for when they asked "Can we ourselves implement something like 'notify' in our properties. Is there a graceful solution for implementing INotifyPropertyChanged in your class"Claustrophobia
This is the only graceful solution really, and it does work flawlessly as @CADbloke said. And I was skeptic about the weaver as well, but I checked/rechecked the IL code behind and it's perfect, it's simple, does all you need and none else. It also hooks and calls whatever method name you have designated in the base class for it, whether NotifyOnProp..., OnNotify... doesn't matter, so works well with any base class that you might have and that implements INotify...Boucicault
You can easily double-check what the weaver is doing, have a look at the build output window, it lists all the PropertyChanged things it has weaved. Using the VScolorOutput extension with the regex pattern "Fody/.*?:",LogCustom2,True highlights it in the "Custom 2" color. I made it bright pink so it's easy to find. Just Fody everything, it's the neatest way to do anything that has lots of repetitive typing.Precarious
@mahmoudnezarsarhan no, it's not, I remember there was a slight change in the way it has to be configured, but Fody PropertyChanged is still alive and active.Corriveau
It does seem to have been removed from FodyProsaism
I agree @Damien. As of version 3.4.0 this property is deprecated. using the AddINotifyPropertyChangedInterfaceAttribute, as recommended by documentation, works for me.Misconstrue
S
72

I think people should pay a little more attention to performance; it really does impact the UI when there are a lot of objects to be bound (think of a grid with 10,000+ rows), or if the object's value changes frequently (real-time monitoring app).

I took various implementation found here and elsewhere and did a comparison; check it out perfomance comparison of INotifyPropertyChanged implementations.


Here is a peek at the result Implemenation vs Runtime

Stocker answered 22/8, 2009 at 9:34 Comment(6)
-1 : there is no performance overhead : CallerMemberName are changed into literal values at compile time. Just try and decompile your app.Majesty
here is the according question and answer: #22581123Hierarchize
@JYL, you are correct that CallerMemberName did not add a large overhead. I must have implemented something wrong last time I tried it. I will update the blog and answer to reflect the benchmark for CallerMemberName and Fody implementation later.Stocker
If you have a grid of 10,000+ in the UI then you should probably be combining approaches to handle performance, like paging where you only show 10, 50, 100, 250 hits per page...Waterless
Austin Rhymer , if you have larg data + 50 use data virtualisation no need to load all data it will load only the data that is visible on the current scolling displayed area !Mochun
The link is dead, so this is no longer useful.Concierge
S
40

I introduce a Bindable class in my blog at http://timoch.com/blog/2013/08/annoyed-with-inotifypropertychange/ Bindable uses a dictionary as a property bag. 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
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    protected T Get<T>([CallerMemberName] string name = null) {
        Debug.Assert(name != null, "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>
    /// <remarks>Use this overload when implicitly naming the property</remarks>
    protected void Set<T>(T value, [CallerMemberName] string name = null) {
        Debug.Assert(name != null, "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) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

It can be used like this:

public class Contact : Bindable {
    public string FirstName {
        get { return Get<string>(); }
        set { Set(value); }
    }
}
Sabo answered 22/8, 2009 at 9:34 Comment(8)
This is a nice solution, but the only downside is that there's a small performance hit involving boxing / unboxing.Chadburn
@Chadburn if we are down to measuring the impact of boxing/unboxing, then we may as well remove the usage of the dictionary and provide Get/Set signatures that take a ref backing field. We may as well provide the means for caching PropertyChangedEventArgs objectsSabo
I would suggest to use protected T Get<T>(T defaultValue, [CallerMemberName] string name = null) and also check if (_properties.ContainsKey(name) && Equals(value, Get<T>(default(T), name))) in Set (to raise & save when first set to default value)Discomfit
+1 for a great solution! I wish I had discovered this answer sooner. Incidentally, quite a few related concerns can be solved much easier with a dictionary-based INotifyPropertyChanged than if each property had its own backing field: for example, creating & restoring object snapshots (think IEditableObject).Thermodynamics
@Discomfit adding support for custom default values can be useful for sure, however you should be careful to only raise the changed event when the value actually changed. Setting a property to the same value it had should not raise events. I must admit in most cases it's harmless, howeverI've been bit quite a few times with properties being set thousands of time to the same value with events destroying UI responsiveness.Sabo
@stakx I have a few applications that build on this to support the memento pattern for undo/redo or to enable the unit of work pattern in applications where nhibernate isn't usableSabo
I really like this particular solution: short notation, no dynamic proxy stuff, no IL-meddling, etc. Although, you can make it shorter by removing the need to specify T every time for Get by making Get return dynamic. I know, this impacts runtime performance, but now the code for getters and setters can finally be always the same and in one line, praise the Lord! P.S. you should take additional care inside your Get method (one time when you write the base class) when returning default values for valuetypes as dynamic. Be sure to always return correct default values (it can be done)Kolk
Very interesting approach to prevent the burden of backing fields, thanks.Zack
S
17

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.

Sanmiguel answered 22/8, 2009 at 9:34 Comment(1)
A free tool called Fody seems to do the same thing, functioning as a generic compile-time code injector. It's downloadable in Nuget, as are its PropertyChanged and PropertyChanging plugin packages.Tletski
G
13

It's 2022. Now there's an official solution.

Use the MVVM source generators in Microsoft MVVM Toolkit.

This

[ObservableProperty]
private string? name;

will generate:

private string? name;

public string? Name
{
    get => name;
    set
    {
        if (!EqualityComparer<string?>.Default.Equals(name, value))
        {
            OnNameChanging(value);
            OnPropertyChanging();
            name = value;
            OnNameChanged(value);
            OnPropertyChanged();
        }
    }
}

// Property changing / changed listener
partial void OnNameChanging(string? value);
partial void OnNameChanged(string? value);

protected void OnPropertyChanging([CallerMemberName] string? propertyName = null)
{
    PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
}

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

It supports .NET standard 2.0 and .NET >= 5.0.

Going answered 22/8, 2009 at 9:34 Comment(4)
What is quite pain with this generator is that you have to change your properties to fields. In our large project it took me a while.Odor
Yet another workaround for something that shouldn't require it, but this time with tons of magic :|Chenab
@Chenab It's not exactly "tons of magic". The partial classes which are generated are easy to see. It's just using an attribute to generate the equivalent public properties from your private fields, and shoving all that boilerplate in a partial class.Classicism
@Classicism the language should be designed in such a way that it doesn't require generating code and partial classes for obvious and natural design patterns.Chenab
C
13

Yes, better way certainly exists. Here it is:

Step by step tutorial shrank by me, based on this useful article.

  • Create new project
  • Install castle core package into the project

Install-Package Castle.Core

  • Install mvvm light libraries only

Install-Package MvvmLightLibs

  • Add two classes in project:

NotifierInterceptor

public class NotifierInterceptor : IInterceptor
    {
        private PropertyChangedEventHandler handler;
        public static Dictionary<String, PropertyChangedEventArgs> _cache =
          new Dictionary<string, PropertyChangedEventArgs>();

        public void Intercept(IInvocation invocation)
        {
            switch (invocation.Method.Name)
            {
                case "add_PropertyChanged":
                    handler = (PropertyChangedEventHandler)
                              Delegate.Combine(handler, (Delegate)invocation.Arguments[0]);
                    invocation.ReturnValue = handler;
                    break;
                case "remove_PropertyChanged":
                    handler = (PropertyChangedEventHandler)
                              Delegate.Remove(handler, (Delegate)invocation.Arguments[0]);
                    invocation.ReturnValue = handler;
                    break;
                default:
                    if (invocation.Method.Name.StartsWith("set_"))
                    {
                        invocation.Proceed();
                        if (handler != null)
                        {
                            var arg = retrievePropertyChangedArg(invocation.Method.Name);
                            handler(invocation.Proxy, arg);
                        }
                    }
                    else invocation.Proceed();
                    break;
            }
        }

        private static PropertyChangedEventArgs retrievePropertyChangedArg(String methodName)
        {
            PropertyChangedEventArgs arg = null;
            _cache.TryGetValue(methodName, out arg);
            if (arg == null)
            {
                arg = new PropertyChangedEventArgs(methodName.Substring(4));
                _cache.Add(methodName, arg);
            }
            return arg;
        }
    }

ProxyCreator

public class ProxyCreator
{
    public static T MakeINotifyPropertyChanged<T>() where T : class, new()
    {
        var proxyGen = new ProxyGenerator();
        var proxy = proxyGen.CreateClassProxy(
          typeof(T),
          new[] { typeof(INotifyPropertyChanged) },
          ProxyGenerationOptions.Default,
          new NotifierInterceptor()
          );
        return proxy as T;
    }
}
  • Create your view model, for example:

-

 public class MainViewModel
    {
        public virtual string MainTextBox { get; set; }

        public RelayCommand TestActionCommand
        {
            get { return new RelayCommand(TestAction); }
        }

        public void TestAction()
        {
            Trace.WriteLine(MainTextBox);
        }
    }
  • Put bindings into xaml:

    <TextBox Text="{Binding MainTextBox}" ></TextBox>
    <Button Command="{Binding TestActionCommand}" >Test</Button>
    
  • Put line of code in code-behind file MainWindow.xaml.cs like this:

DataContext = ProxyCreator.MakeINotifyPropertyChanged<MainViewModel>();

  • Enjoy.

enter image description here

Attention!!! All bounded properties should be decorated with keyword virtual because they used by castle proxy for overriding.

Content answered 22/8, 2009 at 9:34 Comment(2)
I'm interested to know which version of Castle you are using. I am using 3.3.0 and the CreateClassProxy method does not have those parameters: type, interfaces to apply, interceptors.Vanpelt
Nevermind, I was using the generic CreateClassProxy<T> method. Much different ...hmmm, wondering why so limited with the generic method. :(Vanpelt
M
8

A very AOP-like approach is to inject the INotifyPropertyChanged stuff onto an already instantiated object on the fly. You can do this with something like Castle DynamicProxy. Here is an article that explains the technique:

Adding INotifyPropertyChanged to an existing object

Mariano answered 22/8, 2009 at 9:34 Comment(0)
W
5

Whilst there are obviously lots of ways to do this, with the exception of the AOP magic answers, none of the answers seem to look at setting a Model's property directly from the view model without having a local field to reference.

The issue is you can't reference a property. However, you can use an Action to set that property.

protected bool TrySetProperty<T>(Action<T> property, T newValue, T oldValue, [CallerMemberName] string propertyName = null)
{
    if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
    {
        return false;
    }

    property(newValue);
    RaisePropertyChanged(propertyName);
    return true;
}

This can be used like the following code extract.

public int Prop {
    get => model.Prop;
    set => TrySetProperty(x => model.Prop = x, value, model.Prop);
}

Check out this BitBucket repo for a full implementation of the method and a few different ways of achieving the same result, including a method that uses LINQ and a method that uses reflection. Do note that these methods are slower performance wise.

Whisky answered 22/8, 2009 at 9:34 Comment(2)
I've been looking for this. Thanks for sharing your implementation. Are you still using this method?Terrel
@Terrel I haven't really done much WPF development recently but in my last WPF project, I did use this method, along with some of the others in NotifyPropertyChange from my repo that I linkedWhisky
T
5

All these answer are very nice.

My solution is using the code snippets to do the job.

This uses the simplest call to PropertyChanged event.

Save this snippet and use it as you use 'fullprop' snippet.

the location can be found at 'Tools\Code Snippet Manager...' menu at Visual Studio.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>inotifypropfull</Title>
            <Shortcut>inotifypropfull</Shortcut>
            <HelpUrl>http://ofirzeitoun.wordpress.com/</HelpUrl>
            <Description>Code snippet for property and backing field with notification</Description>
            <Author>Ofir Zeitoun</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
                <Literal>
                    <ID>field</ID>
                    <ToolTip>The variable backing this property</ToolTip>
                    <Default>myVar</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[private $type$ $field$;

    public $type$ $property$
    {
        get { return $field$;}
        set { 
            $field$ = value;
            var temp = PropertyChanged;
            if (temp != null)
            {
                temp(this, new PropertyChangedEventArgs("$property$"));
            }
        }
    }
    $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

You can modify the call as you like (to use the above solutions)

Taxidermy answered 22/8, 2009 at 9:34 Comment(0)
A
5

Look here : http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx

It's written in German, but you can download the ViewModelBase.cs. All the comments in the cs-File are written in English.

With this ViewModelBase-Class it is possible to implement bindable properties similar to the well known Dependency Properties :

public string SomeProperty
{
    get { return GetValue( () => SomeProperty ); }
    set { SetValue( () => SomeProperty, value ); }
}
Agone answered 22/8, 2009 at 9:34 Comment(1)
Link is broken.Crestfallen
G
5

Let me introduce my own approach called Yappi. It belongs to Runtime proxy|derived class generators, adding new functionality to an existing object or type, like Caste Project's Dynamic Proxy.

It allows to implement INotifyPropertyChanged once in base class, and then declare derived classes in following style, still supporting INotifyPropertyChanged for new properties:

public class Animal:Concept
{
    protected Animal(){}
    public virtual string Name { get; set; }
    public virtual int Age { get; set; }
}

Complexity of derived class or proxy construction can be hidden behind the following line:

var animal = Concept.Create<Animal>.New();

And all INotifyPropertyChanged implementation work can be done like this:

public class Concept:INotifyPropertyChanged
{
    //Hide constructor
    protected Concept(){}

    public static class Create<TConcept> where TConcept:Concept
    {
        //Construct derived Type calling PropertyProxy.ConstructType
        public static readonly Type Type = PropertyProxy.ConstructType<TConcept, Implementation<TConcept>>(new Type[0], true);
        //Create constructing delegate calling Constructor.Compile
        public static Func<TConcept> New = Constructor.Compile<Func<TConcept>>(Type);
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
    {
        var caller = PropertyChanged;
        if(caller!=null)
        {
            caller(this, eventArgs);
        }
    }

    //define implementation
    public class Implementation<TConcept> : DefaultImplementation<TConcept> where TConcept:Concept
    {
        public override Func<TBaseType, TResult> OverrideGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
        {
            return PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
        }
        /// <summary>
        /// Overriding property setter implementation.
        /// </summary>
        /// <typeparam name="TBaseType">Base type for implementation. TBaseType must be TConcept, and inherits all its constraints. Also TBaseType is TDeclaringType.</typeparam>
        /// <typeparam name="TDeclaringType">Type, declaring property.</typeparam>
        /// <typeparam name="TConstructedType">Constructed type. TConstructedType is TDeclaringType and TBaseType.</typeparam>
        /// <typeparam name="TResult">Type of property.</typeparam>
        /// <param name="property">PropertyInfo of property.</param>
        /// <returns>Delegate, corresponding to property setter implementation.</returns>
        public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
        {
            //This code called once for each declared property on derived type's initialization.
            //EventArgs instance is shared between all events for each concrete property.
            var eventArgs = new PropertyChangedEventArgs(property.Name);
            //get delegates for base calls.
            Action<TBaseType, TResult> setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(property.Name);
            Func<TBaseType, TResult> getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);

            var comparer = EqualityComparer<TResult>.Default;

            return (pthis, value) =>
            {//This code executes each time property setter is called.
                if (comparer.Equals(value, getter(pthis))) return;
                //base. call
                setter(pthis, value);
                //Directly accessing Concept's protected method.
                pthis.OnPropertyChanged(eventArgs);
            };
        }
    }
}

It is fully safe for refactoring, uses no reflection after type construction and fast enough.

Guenevere answered 22/8, 2009 at 9:34 Comment(2)
Why do you need TDeclaration type parameter on PropertyImplementation? Surely you can find appropriate type to call (not callvirt) the getter/setter from with only TImplementation?Gondar
TImplementation works in most of cases. Exceptions are: 1. Properties redefined with "new" C# keyvord. 2. Properties of explicit interface implementation.Guenevere
V
4

Based on the answer by Thomas which was adapted from an answer by Marc I've turned the reflecting property changed code into a base class:

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) 
            handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        var me = selectorExpression.Body as MemberExpression;

        // Nullable properties can be nested inside of a convert function
        if (me == null)
        {
            var ue = selectorExpression.Body as UnaryExpression;
            if (ue != null)
                me = ue.Operand as MemberExpression;
        }

        if (me == null)
            throw new ArgumentException("The body must be a member expression");

        OnPropertyChanged(me.Member.Name);
    }

    protected void SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression, params Expression<Func<object>>[] additonal)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return;
        field = value;
        OnPropertyChanged(selectorExpression);
        foreach (var item in additonal)
            OnPropertyChanged(item);
    }
}

Usage is the same as Thomas' answer except that you can pass additional properties to notify for. This was necessary to handle calculated columns which need to be refreshed in a grid.

private int _quantity;
private int _price;

public int Quantity 
{ 
    get { return _quantity; } 
    set { SetField(ref _quantity, value, () => Quantity, () => Total); } 
}
public int Price 
{ 
    get { return _price; } 
    set { SetField(ref _price, value, () => Price, () => Total); } 
}
public int Total { get { return _price * _quantity; } }

I have this driving a collection of items stored in a BindingList exposed via a DataGridView. It has eliminated the need for me to do manual Refresh() calls to the grid.

Vincevincelette answered 22/8, 2009 at 9:34 Comment(0)
I
3

I keep this around as a snippet. C# 6 adds some nice syntax for invoking the handler.

// INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

private void Set<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
    if (EqualityComparer<T>.Default.Equals(property, value) == false)
    {
        property = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
Isobel answered 22/8, 2009 at 9:34 Comment(0)
H
3

I created an Extension Method in my base Library for reuse:

public static class INotifyPropertyChangedExtensions
{
    public static bool SetPropertyAndNotify<T>(this INotifyPropertyChanged sender,
               PropertyChangedEventHandler handler, ref T field, T value, 
               [CallerMemberName] string propertyName = "",
               EqualityComparer<T> equalityComparer = null)
    {
        bool rtn = false;
        var eqComp = equalityComparer ?? EqualityComparer<T>.Default;
        if (!eqComp.Equals(field,value))
        {
            field = value;
            rtn = true;
            if (handler != null)
            {
                var args = new PropertyChangedEventArgs(propertyName);
                handler(sender, args);
            }
        }
        return rtn;
    }
}

This works with .Net 4.5 because of CallerMemberNameAttribute. If you want to use it with an earlier .Net version you have to change the method declaration from: ...,[CallerMemberName] string propertyName = "", ... to ...,string propertyName, ...

Usage:

public class Dog : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            this.SetPropertyAndNotify(PropertyChanged, ref _name, value);
        }
    }
}
Hearsay answered 22/8, 2009 at 9:34 Comment(0)
B
2

I came up with this base class to implement the observable pattern, pretty much does what you need ("automatically" implementing the set and get). I spent line an hour on this as prototype, so it doesn't have many unit tests, but proves the concept. Note it uses the Dictionary<string, ObservablePropertyContext> to remove the need for private fields.

  public class ObservableByTracking<T> : IObservable<T>
  {
    private readonly Dictionary<string, ObservablePropertyContext> _expando;
    private bool _isDirty;

    public ObservableByTracking()
    {
      _expando = new Dictionary<string, ObservablePropertyContext>();

      var properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
      foreach (var property in properties)
      {
        var valueContext = new ObservablePropertyContext(property.Name, property.PropertyType)
        {
          Value = GetDefault(property.PropertyType)
        };

        _expando[BuildKey(valueContext)] = valueContext;
      }
    }

    protected void SetValue<T>(Expression<Func<T>> expression, T value)
    {
      var keyContext = GetKeyContext(expression);
      var key = BuildKey(keyContext.PropertyName, keyContext.PropertyType);

      if (!_expando.ContainsKey(key))
      {
        throw new Exception($"Object doesn't contain {keyContext.PropertyName} property.");
      }

      var originalValue = (T)_expando[key].Value;
      if (EqualityComparer<T>.Default.Equals(originalValue, value))
      {
        return;
      }

      _expando[key].Value = value;
      _isDirty = true;
    }

    protected T GetValue<T>(Expression<Func<T>> expression)
    {
      var keyContext = GetKeyContext(expression);
      var key = BuildKey(keyContext.PropertyName, keyContext.PropertyType);

      if (!_expando.ContainsKey(key))
      {
        throw new Exception($"Object doesn't contain {keyContext.PropertyName} property.");
      }

      var value = _expando[key].Value;
      return (T)value;
    }

    private KeyContext GetKeyContext<T>(Expression<Func<T>> expression)
    {
      var castedExpression = expression.Body as MemberExpression;
      if (castedExpression == null)
      {
        throw new Exception($"Invalid expression.");
      }

      var parameterName = castedExpression.Member.Name;

      var propertyInfo = castedExpression.Member as PropertyInfo;
      if (propertyInfo == null)
      {
        throw new Exception($"Invalid expression.");
      }

      return new KeyContext {PropertyType = propertyInfo.PropertyType, PropertyName = parameterName};
    }

    private static string BuildKey(ObservablePropertyContext observablePropertyContext)
    {
      return $"{observablePropertyContext.Type.Name}.{observablePropertyContext.Name}";
    }

    private static string BuildKey(string parameterName, Type type)
    {
      return $"{type.Name}.{parameterName}";
    }

    private static object GetDefault(Type type)
    {
      if (type.IsValueType)
      {
        return Activator.CreateInstance(type);
      }
      return null;
    }

    public bool IsDirty()
    {
      return _isDirty;
    }

    public void SetPristine()
    {
      _isDirty = false;
    }

    private class KeyContext
    {
      public string PropertyName { get; set; }
      public Type PropertyType { get; set; }
    }
  }

  public interface IObservable<T>
  {
    bool IsDirty();
    void SetPristine();
  }

Here's the usage

public class ObservableByTrackingTestClass : ObservableByTracking<ObservableByTrackingTestClass>
  {
    public ObservableByTrackingTestClass()
    {
      StringList = new List<string>();
      StringIList = new List<string>();
      NestedCollection = new List<ObservableByTrackingTestClass>();
    }

    public IEnumerable<string> StringList
    {
      get { return GetValue(() => StringList); }
      set { SetValue(() => StringIList, value); }
    }

    public IList<string> StringIList
    {
      get { return GetValue(() => StringIList); }
      set { SetValue(() => StringIList, value); }
    }

    public int IntProperty
    {
      get { return GetValue(() => IntProperty); }
      set { SetValue(() => IntProperty, value); }
    }

    public ObservableByTrackingTestClass NestedChild
    {
      get { return GetValue(() => NestedChild); }
      set { SetValue(() => NestedChild, value); }
    }

    public IList<ObservableByTrackingTestClass> NestedCollection
    {
      get { return GetValue(() => NestedCollection); }
      set { SetValue(() => NestedCollection, value); }
    }

    public string StringProperty
    {
      get { return GetValue(() => StringProperty); }
      set { SetValue(() => StringProperty, value); }
    }
  }
Bairam answered 22/8, 2009 at 9:34 Comment(0)
E
2

I have written an article that helps with this (https://msdn.microsoft.com/magazine/mt736453). You can use the SolSoft.DataBinding NuGet package. Then you can write code like this:

public class TestViewModel : IRaisePropertyChanged
{
  public TestViewModel()
  {
    this.m_nameProperty = new NotifyProperty<string>(this, nameof(Name), null);
  }

  private readonly NotifyProperty<string> m_nameProperty;
  public string Name
  {
    get
    {
      return m_nameProperty.Value;
    }
    set
    {
      m_nameProperty.SetValue(value);
    }
  }

  // Plus implement IRaisePropertyChanged (or extend BaseViewModel)
}

Benefits:

  1. base class is optional
  2. no reflection on every 'set value'
  3. can have properties that depend on other properties, and they all automatically raise the appropriate events (article has an example of this)
Endow answered 22/8, 2009 at 9:34 Comment(0)
T
2

Here is a Unity3D or non-CallerMemberName version of NotifyPropertyChanged

public abstract class Bindable : MonoBehaviour, INotifyPropertyChanged
{
    private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
    private static readonly StackTrace stackTrace = new StackTrace();
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    ///     Resolves a Property's name from a Lambda Expression passed in.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="property"></param>
    /// <returns></returns>
    internal string GetPropertyName<T>(Expression<Func<T>> property)
    {
        var expression = (MemberExpression) property.Body;
        var propertyName = expression.Member.Name;

        Debug.AssertFormat(propertyName != null, "Bindable Property shouldn't be null!");
        return propertyName;
    }

    #region Notification Handlers

    /// <summary>
    ///     Notify's all other objects listening that a value has changed for nominated propertyName
    /// </summary>
    /// <param name="propertyName"></param>
    internal void NotifyOfPropertyChange(string propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    /// <summary>
    ///     Notifies subscribers of the property change.
    /// </summary>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="property">The property expression.</param>
    internal void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
    {
        var propertyName = GetPropertyName(property);
        NotifyOfPropertyChange(propertyName);
    }

    /// <summary>
    ///     Raises the <see cref="PropertyChanged" /> event directly.
    /// </summary>
    /// <param name="e">The <see cref="PropertyChangedEventArgs" /> instance containing the event data.</param>
    internal void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    #endregion

    #region Getters

    /// <summary>
    ///     Gets the value of a property
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    internal T Get<T>(Expression<Func<T>> property)
    {
        var propertyName = GetPropertyName(property);
        return Get<T>(GetPropertyName(property));
    }

    /// <summary>
    ///     Gets the value of a property automatically based on its caller.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    internal T Get<T>()
    {
        var name = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
        return Get<T>(name);
    }

    /// <summary>
    ///     Gets the name of a property based on a string.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    internal T Get<T>(string name)
    {
        object value = null;
        if (_properties.TryGetValue(name, out value))
            return value == null ? default(T) : (T) value;
        return default(T);
    }

    #endregion

    #region Setters

    /// <summary>
    ///     Sets the value of a property whilst automatically looking up its caller name.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    internal void Set<T>(T value)
    {
        var propertyName = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
        Set(value, propertyName);
    }

    /// <summary>
    ///     Sets the value of a property
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <param name="name"></param>
    internal void Set<T>(T value, string propertyName)
    {
        Debug.Assert(propertyName != null, "name != null");
        if (Equals(value, Get<T>(propertyName)))
            return;
        _properties[propertyName] = value;
        NotifyOfPropertyChange(propertyName);
    }

    /// <summary>
    ///     Sets the value of a property based off an Expression (()=>FieldName)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <param name="property"></param>
    internal void Set<T>(T value, Expression<Func<T>> property)
    {
        var propertyName = GetPropertyName(property);

        Debug.Assert(propertyName != null, "name != null");

        if (Equals(value, Get<T>(propertyName)))
            return;
        _properties[propertyName] = value;
        NotifyOfPropertyChange(propertyName);
    }

    #endregion
}

This code enables you to write property backing fields like this:

  public string Text
    {
        get { return Get<string>(); }
        set { Set(value); }
    }

Furthermore, in resharper if you create a pattern/search snippet you can then also automate you're workflow by converting simple prop fields into the above backing.

Search Pattern:

public $type$ $fname$ { get; set; }

Replace Pattern:

public $type$ $fname$
{
    get { return Get<$type$>(); }
    set { Set(value); }
}
Tamarisk answered 22/8, 2009 at 9:34 Comment(0)
C
2

I resolved in This Way (it's a little bit laboriouse, but it's surely the faster in runtime).

In VB (sorry, but I think it's not hard translate it in C#), I make this substitution with RE:

(?<Attr><(.*ComponentModel\.)Bindable\(True\)>)( |\r\n)*(?<Def>(Public|Private|Friend|Protected) .*Property )(?<Name>[^ ]*) As (?<Type>.*?)[ |\r\n](?![ |\r\n]*Get)

with:

Private _${Name} As ${Type}\r\n${Attr}\r\n${Def}${Name} As ${Type}\r\nGet\r\nReturn _${Name}\r\nEnd Get\r\nSet (Value As ${Type})\r\nIf _${Name} <> Value Then \r\n_${Name} = Value\r\nRaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("${Name}"))\r\nEnd If\r\nEnd Set\r\nEnd Property\r\n

This transofrm all code like this:

<Bindable(True)>
Protected Friend Property StartDate As DateTime?

In

Private _StartDate As DateTime?
<Bindable(True)>
Protected Friend Property StartDate As DateTime?
    Get
        Return _StartDate
    End Get
    Set(Value As DateTime?)
        If _StartDate <> Value Then
            _StartDate = Value
            RaiseEvent PropertyChange(Me, New ComponentModel.PropertyChangedEventArgs("StartDate"))
        End If
    End Set
End Property

And If I want to have a more readable code, I can be the opposite just making the following substitution:

Private _(?<Name>.*) As (?<Type>.*)[\r\n ]*(?<Attr><(.*ComponentModel\.)Bindable\(True\)>)[\r\n ]*(?<Def>(Public|Private|Friend|Protected) .*Property )\k<Name> As \k<Type>[\r\n ]*Get[\r\n ]*Return _\k<Name>[\r\n ]*End Get[\r\n ]*Set\(Value As \k<Type>\)[\r\n ]*If _\k<Name> <> Value Then[\r\n ]*_\k<Name> = Value[\r\n ]*RaiseEvent PropertyChanged\(Me, New (.*ComponentModel\.)PropertyChangedEventArgs\("\k<Name>"\)\)[\r\n ]*End If[\r\n ]*End Set[\r\n ]*End Property

With

${Attr} ${Def} ${Name} As ${Type}

I throw to replace the IL code of the set method, but I can't write a lot of compiled code in IL... If a day I write it, I'll say you!

Ceilidh answered 22/8, 2009 at 9:34 Comment(0)
T
2

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); }
    }
}
Taxidermy answered 22/8, 2009 at 9:34 Comment(5)
Is that fast ? Isn't access to stack frame bound to some permission requirement ? Is that robust in a context of using async/await ?Incarnate
@StéphaneGourichon No, it isn't. Accessing the stack frame means a considerable performance hit on most cases.Deposal
Yes there is, you can see it at codereview.stackexchange.com/questions/13823/…Taxidermy
Note that inlining may hide the get_Foo method in Release mode.Swordbill
This was needed/useful prior to CallerMemberName, was that the idea?Concierge
S
2

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.

Sanburn answered 22/8, 2009 at 9:34 Comment(1)
any disadvantages of using this?Goines
M
2

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).

Marlee answered 22/8, 2009 at 9:34 Comment(0)
C
1

A new (2023) solution is to use Metalama. Like PostSharp and Fody, Metalama injects the pattern implementation during the build. However, unlike these tools, Metalama does it by manipulating the Roslyn AST instead of doing IL rewriting. Concretely, you get more transparency into the code being generated -- and you can debug it.

Here is the aspect implementation according to their documentation

using Metalama.Framework.Aspects;
using Metalama.Framework.Code;
using System.ComponentModel;

[Inheritable]
internal class NotifyPropertyChangedAttribute : TypeAspect
{
    public override void BuildAspect( IAspectBuilder<INamedType> builder )
    {
        builder.Advice.ImplementInterface( builder.Target, typeof(INotifyPropertyChanged), OverrideStrategy.Ignore );

        foreach ( var property in builder.Target.Properties.Where( p =>
                     !p.IsAbstract && p.Writeability == Writeability.All ) )
        {
            builder.Advice.OverrideAccessors( property, null, nameof(this.OverridePropertySetter) );
        }
    }

    [InterfaceMember]
    public event PropertyChangedEventHandler? PropertyChanged;

    [Introduce( WhenExists = OverrideStrategy.Ignore )]
    protected void OnPropertyChanged( string name ) =>
        this.PropertyChanged?.Invoke( meta.This, new PropertyChangedEventArgs( name ) );

    [Template]
    private dynamic OverridePropertySetter( dynamic value )
    {
        if ( value != meta.Target.Property.Value )
        {
            meta.Proceed();
            this.OnPropertyChanged( meta.Target.Property.Name );
        }

        return value;
    }
}

A more complex implementation of INotifyPropertyChanges supporting child objects and properties depending on expressions like this.A.B.C (quite frequent in MVVM architectures) is under development on GitHub at the time of writing.

Cunha answered 22/8, 2009 at 9:34 Comment(0)
U
1

I'm writing a library that deals with INotifyPropertyChanged, and the main idea is using a dynamic proxy to notify changes.

the repo is here: CaulyKan/NoMorePropertyChanged

with this library, you can write:

    public dynamic Test1Binding { get; set; }
    public TestDTO Test1
    {
        get { return (TestDTO)Test1Binding; }
        set { SetBinding(nameof(Test1Binding), value); }
    }

Then make all bindings and modifications go to Test1Binding, which will notify PropertyChange and CollectionChanged automatically no matter how complex TestDTO is.

it can also handle dependencies.

    [DependsOn("Test1Binding.TestString")]
    public string Test2
    {
        get { return Test1Binding.TestString; }
    }

Please give me some suggestions.

Unstick answered 22/8, 2009 at 9:34 Comment(0)
M
1

I suggest to use ReactiveProperty. This is the shortest method except Fody.

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    ...
    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

instead

public class Data
{
    // Don't need boiler-plate and INotifyPropertyChanged

    // props
    public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>();
}

(DOCS)

Mauro answered 22/8, 2009 at 9:34 Comment(0)
U
1

I realize this question already has a gazillion answers, but none of them felt quite right for me. My issue is I don't want any performance hits and am willing to put up with a little verbosity for that reason alone. I also don't care too much for auto properties either, which led me to the following solution:

public abstract class AbstractObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual bool SetValue<TKind>(ref TKind Source, TKind NewValue, params string[] Notify)
    {
        //Set value if the new value is different from the old
        if (!Source.Equals(NewValue))
        {
            Source = NewValue;

            //Notify all applicable properties
            foreach (var i in Notify)
                OnPropertyChanged(i);

            return true;
        }

        return false;
    }

    public AbstractObject()
    {
    }
}

In other words, the above solution is convenient if you don't mind doing this:

public class SomeObject : AbstractObject
{
    public string AnotherProperty
    {
        get
        {
            return someProperty ? "Car" : "Plane";
        }
    }

    bool someProperty = false;
    public bool SomeProperty
    {
        get
        {
            return someProperty;
        }
        set
        {
            SetValue(ref someProperty, value, "SomeProperty", "AnotherProperty");
        }
    }

    public SomeObject() : base()
    {
    }
}

Pros

  • No reflection
  • Only notifies if old value != new value
  • Notify multiple properties at once

Cons

  • No auto properties (you can add support for both, though!)
  • Some verbosity
  • Boxing (small performance hit?)

Alas, it is still better than doing this,

set
{
    if (!someProperty.Equals(value))
    {
        someProperty = value;
        OnPropertyChanged("SomeProperty");
        OnPropertyChanged("AnotherProperty");
    }
}

For every single property, which becomes a nightmare with the additional verbosity ;-(

Note, I do not claim this solution is better performance-wise compared to the others, just that it is a viable solution for those who don't like the other solutions presented.

Unpolled answered 22/8, 2009 at 9:34 Comment(0)
G
1

I use the following extension method (using C# 6.0) to make the INPC implemenation as easy as possible:

public static bool ChangeProperty<T>(this PropertyChangedEventHandler propertyChanged, ref T field, T value, object sender,
    IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
    if (comparer == null)
        comparer = EqualityComparer<T>.Default;

    if (comparer.Equals(field, value))
    {
        return false;
    }
    else
    {
        field = value;
        propertyChanged?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
        return true;
    }
}

The INPC implementation boils down to (you can either implement this every time or create a base class):

public class INPCBaseClass: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool changeProperty<T>(ref T field, T value,
        IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
    {
        return PropertyChanged.ChangeProperty(ref field, value, this, comparer, propertyName);
    }
}

Then write your properties like this:

private string testProperty;
public string TestProperty
{
    get { return testProperty; }
    set { changeProperty(ref testProperty, value); }
}

NOTE: You can omit the [CallerMemberName] declaration in the extension method, if you want, but I wanted to keep it flexible.

If you have properties without a backing field you can overload changeProperty:

protected bool changeProperty<T>(T property, Action<T> set, T value,
    IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
    bool ret = changeProperty(ref property, value, comparer, propertyName);
    if (ret)
        set(property);
    return ret;
}

An example use would be:

public string MyTestProperty
{
    get { return base.TestProperty; }
    set { changeProperty(base.TestProperty, (x) => { base.TestProperty = x; }, value); }
}
Gaudy answered 22/8, 2009 at 9:34 Comment(0)
D
1

Use this

using System;
using System.ComponentModel;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;


public static class ObservableFactory
{
    public static T Create<T>(T target)
    {
        if (!typeof(T).IsInterface)
            throw new ArgumentException("Target should be an interface", "target");

        var proxy = new Observable<T>(target);
        return (T)proxy.GetTransparentProxy();
    }
}

internal class Observable<T> : RealProxy, INotifyPropertyChanged, INotifyPropertyChanging
{
    private readonly T target;

    internal Observable(T target)
        : base(ImplementINotify(typeof(T)))
    {
        this.target = target;
    }

    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;

        if (methodCall != null)
        {
            return HandleMethodCall(methodCall);
        }

        return null;
    }

    public event PropertyChangingEventHandler PropertyChanging;
    public event PropertyChangedEventHandler PropertyChanged;



    IMessage HandleMethodCall(IMethodCallMessage methodCall)
    {
        var isPropertySetterCall = methodCall.MethodName.StartsWith("set_");
        var propertyName = isPropertySetterCall ? methodCall.MethodName.Substring(4) : null;

        if (isPropertySetterCall)
        {
            OnPropertyChanging(propertyName);
        }

        try
        {
            object methodCalltarget = target;

            if (methodCall.MethodName == "add_PropertyChanged" || methodCall.MethodName == "remove_PropertyChanged"||
                methodCall.MethodName == "add_PropertyChanging" || methodCall.MethodName == "remove_PropertyChanging")
            {
                methodCalltarget = this;
            }

            var result = methodCall.MethodBase.Invoke(methodCalltarget, methodCall.InArgs);

            if (isPropertySetterCall)
            {
                OnPropertyChanged(methodCall.MethodName.Substring(4));
            }

            return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
        }
        catch (TargetInvocationException invocationException)
        {
            var exception = invocationException.InnerException;
            return new ReturnMessage(exception, methodCall);
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanging(string propertyName)
    {
        var handler = PropertyChanging;
        if (handler != null) handler(this, new PropertyChangingEventArgs(propertyName));
    }

    public static Type ImplementINotify(Type objectType)
    {
        var tempAssemblyName = new AssemblyName(Guid.NewGuid().ToString());

        var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
            tempAssemblyName, AssemblyBuilderAccess.RunAndCollect);

        var moduleBuilder = dynamicAssembly.DefineDynamicModule(
            tempAssemblyName.Name,
            tempAssemblyName + ".dll");

        var typeBuilder = moduleBuilder.DefineType(
            objectType.FullName, TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);

        typeBuilder.AddInterfaceImplementation(objectType);
        typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanged));
        typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanging));
        var newType = typeBuilder.CreateType();
        return newType;
    }
}

}

Dehisce answered 22/8, 2009 at 9:34 Comment(0)
Q
1

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
Quattrocento answered 22/8, 2009 at 9:34 Comment(1)
This is pretty cool, I like it more than expression approach. On the downside, should be slower.Olimpiaolin
K
1

Other things you may want to consider when implementing these sorts of properties is the fact that the INotifyPropertyChang *ed *ing both use event argument classes.

If you have a large number of properties that are being set then the number of event argument class instances can be huge, you should consider caching them as they are one of the areas that a string explosion can occur.

Take a look at this implementation and explanation of why it was conceived.

Josh Smiths Blog

Kilometer answered 22/8, 2009 at 9:34 Comment(0)
E
0

Prism 5 implementation:

public abstract class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual bool SetProperty<T>(ref T storage,
                                          T value,
                                          [CallerMemberName] string propertyName = null)
    {
        if (object.Equals(storage, value)) return false;

        storage = value;
        this.OnPropertyChanged(propertyName);

        return true;
    }

    protected void OnPropertyChanged(string propertyName)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
        {
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    protected void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
    {
        var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
        this.OnPropertyChanged(propertyName);
    }
}

public static class PropertySupport
{
    public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
    {
        if (propertyExpression == null)
        {
            throw new ArgumentNullException("propertyExpression");
        }

        var memberExpression = propertyExpression.Body as MemberExpression;
        if (memberExpression == null)
        {
            throw new ArgumentException("The expression is not a member access expression.", "propertyExpression");
        }

        var property = memberExpression.Member as PropertyInfo;
        if (property == null)
        {
            throw new ArgumentException("The member access expression does not access a property.", "propertyExpression");
        }

        var getMethod = property.GetMethod;
        if (getMethod.IsStatic)
        {
            throw new ArgumentException("The referenced property is a static property.", "propertyExpression");
        }

        return memberExpression.Member.Name;
    }
}
Ewald answered 22/8, 2009 at 9:34 Comment(0)
B
0

=> here my solution with the following features

 public ResourceStatus Status
 {
     get { return _status; }
     set
     {
         _status = value;
         Notify(Npcea.Status,Npcea.Comments);
     }
 }
  1. no refelction
  2. short notation
  3. no magic string in your business code
  4. Reusability of PropertyChangedEventArgs across application
  5. Possibility to notify multiple properties in one statement
Brail answered 22/8, 2009 at 9:34 Comment(0)
P
0

Another Idea...

 public class ViewModelBase : INotifyPropertyChanged
{
    private Dictionary<string, object> _propertyStore = new Dictionary<string, object>();
    protected virtual void SetValue<T>(T value, [CallerMemberName] string propertyName="") {
        _propertyStore[propertyName] = value;
        OnPropertyChanged(propertyName);
    }
    protected virtual T GetValue<T>([CallerMemberName] string propertyName = "")
    {
        object ret;
        if (_propertyStore.TryGetValue(propertyName, out ret))
        {
            return (T)ret;
        }
        else
        {
            return default(T);
        }
    }

    //Usage
    //public string SomeProperty {
    //    get { return GetValue<string>();  }
    //    set { SetValue(value); }
    //}

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        var temp = PropertyChanged;
        if (temp != null)
            temp.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
Profusive answered 22/8, 2009 at 9:34 Comment(0)
S
-2

Talk about massive overengineering. This is significantly more complex than just doing it the right way and gives little to no benefit. If your IDE supports code snippets (Visual Studio/MonoDevelop do) then you can make implementing this ridiculously simple. All you'd actually have to type is the type of the property and the property name. The extra three lines of code will be autogenerated.

Sherfield answered 22/8, 2009 at 9:34 Comment(3)
In order to stay away from magic strings, you can also use the code from this blog post: blog.m.jedynak.pl/2009/02/static-typed-propety-names.htmlMulry
Code snippets are nice when you write the code, but can become a pain in maintenance.Uphemia
Code snippets do not solve the problem at all. The issue is magic strings not being renamed when refactoring property names.Stammel

© 2022 - 2024 — McMap. All rights reserved.