Implementing INotifyPropertyChanged - does a better way exist?

348,165

Solution 1

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);
}

Solution 2

As of .Net 4.5 there is finally an easy way to do this.

.Net 4.5 introduces a new 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)

Solution 3

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.

Solution 4

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.

Solution 5

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

Share:
348,165
P.K
Author by

P.K

All code is poetry. But, not every coder is poet. I am far off from being a poet and this keeps me on toes and makes me constantly improve and learn new things.

Updated on July 08, 2022

Comments

  • P.K
    P.K almost 2 years

    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?

  • Thomas Levesque
    Thomas Levesque almost 15 years
    Nice trick Marc ! I suggested an improvement to use a lambda expression instead of the property name, see my answer
  • Anton Tykhyy
    Anton Tykhyy almost 15 years
    DevXpress Xpo does it this way.
  • Marc Gravell
    Marc Gravell almost 15 years
    @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.
  • Thomas Levesque
    Thomas Levesque almost 15 years
    @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" command
  • scoopr
    scoopr over 14 years
    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.html
  • AlSki
    AlSki over 12 years
    There's at least one framework using this method, ReactiveUI.
  • T. Fabre
    T. Fabre over 12 years
    Code snippets are nice when you write the code, but can become a pain in maintenance.
  • abatishchev
    abatishchev over 11 years
    Brilliant! But why is it generic?
  • Daniel Little
    Daniel Little over 11 years
    @abatishchev 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.
  • J. Lennon
    J. Lennon over 11 years
    It was introduced by C # 5.0. It has nothing to do with .net 4.5, but this is a great solution!
  • Daniel Little
    Daniel Little over 11 years
    @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/…
  • J. Lennon
    J. Lennon over 11 years
    @Lavinski change your application to eg .NET 3.5 and see what will work (in vs2012)
  • J. Lennon
    J. Lennon over 11 years
    @Lavinski sorry, I was wrong. CallerMemberName was introduced by other implementations for older versions of .NET (async bridge and microsoft.bcl)
  • Bob
    Bob about 11 years
    @Lavinski It is possible to use this while targeting older framework versions, with a minor tweak: stackoverflow.com/a/13382257/1030702
  • Cocowalla
    Cocowalla almost 11 years
    A great approach, but it does have some shortcomings: awkwardcoder.blogspot.co.uk/2012/07/…
  • Marc Gravell
    Marc Gravell almost 11 years
    @PKKG that means nobody has subscribed to the event
  • Pedro77
    Pedro77 over 10 years
    What about: private void RaisePropertyChanged( [CallerMemberName] string caller = "") { if ( PropertyChanged != null ) { PropertyChanged( this, new PropertyChangedEventArgs( caller ) ); } }
  • Gusdor
    Gusdor over 10 years
    @ThomasLevesque The refactor ability supersedes the performance hit imo. Never pre-optimise where sacrificing maintainability is concerned.
  • Marc Gravell
    Marc Gravell over 10 years
    @Gusdor fortunately, with C#5 there is no need to compromise - you can get the best of both via (as Pedro77 notes) [CallerMemberName]
  • Gusdor
    Gusdor over 10 years
    @MarcGravell I wish I could know this joy. I'm still supporting windows XP and have to use .Net 4 :*(
  • Marc Gravell
    Marc Gravell over 10 years
    @Gusdor 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.
  • itsho
    itsho over 10 years
    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...
  • Triynko
    Triynko over 10 years
    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.
  • JYL
    JYL over 10 years
    -1 : there is no performance overhead : CallerMemberName are changed into literal values at compile time. Just try and decompile your app.
  • Bionic
    Bionic over 10 years
    here is the according question and answer: stackoverflow.com/questions/22580623/…
  • Peijen
    Peijen about 10 years
    @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.
  • Chris Fong
    Chris Fong about 10 years
    @Gusdor [CallerMemberName] is available for .Net 4.0 using the Microsoft.Bcl nuget package! nuget.org/packages/Microsoft.Bcl
  • Dan Bechard
    Dan Bechard about 10 years
    Code snippets do not solve the problem at all. The issue is magic strings not being renamed when refactoring property names.
  • nawfal
    nawfal almost 10 years
    This is pretty cool, I like it more than expression approach. On the downside, should be slower.
  • Jay Carlton
    Jay Carlton over 9 years
    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?
  • Marc Gravell
    Marc Gravell over 9 years
    @Jay there are ways of doing it with static methods so that you only need to declare it once, sure
  • Jay Carlton
    Jay Carlton over 9 years
    I just noticed BindableBase does what I was looking for. Thanks, @Marc.
  • MCattle
    MCattle over 9 years
    This is a nice solution, but the only downside is that there's a small performance hit involving boxing / unboxing.
  • TiMoch
    TiMoch over 9 years
    @MCattle 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 objects
  • Stéphane Gourichon
    Stéphane Gourichon over 9 years
    Is that fast ? Isn't access to stack frame bound to some permission requirement ? Is that robust in a context of using async/await ?
  • Miquel
    Miquel over 9 years
    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)
  • Bruno Brant
    Bruno Brant over 9 years
    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.
  • stakx - no longer contributing
    stakx - no longer contributing over 9 years
    +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).
  • TiMoch
    TiMoch over 9 years
    @Miquel 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.
  • TiMoch
    TiMoch over 9 years
    @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 usable
  • Nathaniel Elkins
    Nathaniel Elkins over 9 years
    @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).
  • Bruno Brant
    Bruno Brant about 9 years
    @StéphaneGourichon No, it isn't. Accessing the stack frame means a considerable performance hit on most cases.
  • Bruno Brant
    Bruno Brant about 9 years
    @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.
  • ASA
    ASA about 9 years
    I believe your whole OnPropertyChanged<T> is obsolete with the nameof operator of C# 6, making this monster a bit sleeker.
  • Thomas Levesque
    Thomas Levesque about 9 years
    @Traubenfuchs, actually, C#5's CallerMemberName attribute makes it even simpler, since you don't need to pass anything at all ...
  • Dib
    Dib about 9 years
    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?
  • Andrew Savinykh
    Andrew Savinykh about 9 years
    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?
  • Kelqualyn
    Kelqualyn almost 9 years
    TImplementation works in most of cases. Exceptions are: 1. Properties redefined with "new" C# keyvord. 2. Properties of explicit interface implementation.
  • Fandi Susanto
    Fandi Susanto over 8 years
    What black magic is this? Can somebody point to some reference or explanation? What is [CallerMemberName] and PropertyChanged?.Invoke?
  • Marc Gravell
    Marc Gravell over 8 years
    @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.
  • evilkos
    evilkos over 8 years
    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)
  • Panagiotis Kanavos
    Panagiotis Kanavos over 8 years
    @MarcGravell perhaps you could clean up the code for C# 6, so this can be avoided? I now regret answering instead of voting to close
  • IAbstract
    IAbstract about 8 years
    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.
  • IAbstract
    IAbstract about 8 years
    Nevermind, I was using the generic CreateClassProxy<T> method. Much different ...hmmm, wondering why so limited with the generic method. :(
  • BrainSlugs83
    BrainSlugs83 about 8 years
    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.
  • Marc Gravell
    Marc Gravell about 8 years
    @BrainSlugs83 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.
  • Stanislav
    Stanislav about 8 years
    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)));
  • Ofir
    Ofir about 8 years
    Yes there is, you can see it at codereview.stackexchange.com/questions/13823/…
  • Ashoat
    Ashoat almost 8 years
    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"
  • jmbpiano
    jmbpiano almost 8 years
    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.
  • evilkos
    evilkos over 7 years
    @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.
  • Adam Goodwin
    Adam Goodwin over 7 years
    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?
  • Marc Gravell
    Marc Gravell over 7 years
    @AdamGoodwin generics do not work with operators like ==
  • Adam Goodwin
    Adam Goodwin over 7 years
    Oops, I knew it would be something obvious – I should have just tried it.
  • Guge
    Guge over 7 years
    Link is broken.
  • NSGaga-mostly-inactive
    NSGaga-mostly-inactive over 7 years
    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...
  • Austin Rhymer
    Austin Rhymer almost 7 years
    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...
  • CAD bloke
    CAD bloke almost 7 years
    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.
  • juFo
    juFo almost 7 years
    any disadvantages of using this?
  • bh_earth0
    bh_earth0 over 6 years
    is there a way to not write that boilerplate in every new class i create ??
  • brakeroo
    brakeroo over 6 years
    Furthermore in C# 6.0 you can use expression body notation: protected void OnPropertyChanged( [CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  • ToolmakerSteve
    ToolmakerSteve over 6 years
    ObservableCollection eliminates the need to implement OnPropertyChanged, for simple situations. See How to: Create and Bind to an ObservableCollection, and ObservableCollection.
  • Andrey Klochkov
    Andrey Klochkov about 6 years
    And for C#7 we can skip the nameof(Name) param to make it a little shorter.
  • Marc Gravell
    Marc Gravell about 6 years
    @AndreyKlochkov you're right - fixed; looks like that segment was added at a later date, but: all fixed
  • JDR
    JDR almost 6 years
    Out of interest: why SetField and not SetProperty?
  • Marc Gravell
    Marc Gravell almost 6 years
    @JDR 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)
  • fstam
    fstam over 5 years
    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"); }
  • Mark Redman
    Mark Redman over 5 years
    should the c# & code be: set => SetField(ref name, value, "name");
  • Marc Gravell
    Marc Gravell over 5 years
    @MarkRedman no; the whole point of [CallerMemberName] is that it provides the caller name automatically
  • Mark Redman
    Mark Redman over 5 years
    @MarcGravell, ok, makes sense, was sure I am am C#7, but indicated a missing parameter...
  • Marc Gravell
    Marc Gravell over 5 years
    @MarkRedman that's odd - just to be sure, I did test it here before I replied, and it compiled fine
  • JAD
    JAD over 5 years
    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.
  • Rhyous
    Rhyous over 5 years
    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.
  • bytecode77
    bytecode77 about 5 years
    Note that inlining may hide the get_Foo method in Release mode.
  • Larry
    Larry almost 5 years
    @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.
  • AFract
    AFract over 4 years
    Very interesting approach to prevent the burden of backing fields, thanks.
  • Bilal
    Bilal about 4 years
    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 !
  • t0b4cc0
    t0b4cc0 about 4 years
    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)
  • Marc Gravell
    Marc Gravell about 4 years
    @t0b4cc0 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 available
  • t0b4cc0
    t0b4cc0 about 4 years
    @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.
  • Marc Gravell
    Marc Gravell about 4 years
    @t0b4cc0 I'd probably need to see it in context as a repro to understand exactly what your scenario is
  • t0b4cc0
    t0b4cc0 about 4 years
    @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 again
  • GisMofx
    GisMofx almost 4 years
    I've been looking for this. Thanks for sharing your implementation. Are you still using this method?
  • Dan
    Dan almost 4 years
    @GisMofx 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 linked
  • Damien
    Damien over 3 years
    It does seem to have been removed from Fody
  • Bill Menees
    Bill Menees about 3 years
    C#9 allows the new PropertyChangedEventArgs(propertyName) expression to be shortened to just new(propertyName).
  • Examath
    Examath over 2 years
    I agree @Damien. As of version 3.4.0 this property is deprecated. using the AddINotifyPropertyChangedInterfaceAttribute, as recommended by documentation, works for me.