3

I develop lot view models which are:

1) All have to implement INotifyPropertyChanged to be bindable to UI.

2) Property setters have to raise PropertyChanged on change.

3) PropertyChanged event has to provide proper property name.

If you (like me) tied of writing something like this:


public string Name 
{
  get 
  { 
    return _name; 
  }
  set 
  { 
    if (_name != value) 
    {
      _name = value;
      RaisePropertyChanged("Name");
    }
  }
} 

Then refactor this method like this and sometimes forget to update property name literal:


string _fundName;
public string FundName 
{
  get 
  { 
    return _fundName; 
  }
  set 
  { 
    if (_fundName != value) 
    {
      _fundName = value;
      RaisePropertyChanged("Name");
    }
  }
} 

And then spend a day to debug why your UI is not refreshing and databinding doesn't work properly.

Then all we need is some kind of magic.

What if I just need to write this:


[Magic] // implicit transformation
public string FundName { get; set; }

or if I have many properties:


[Magic]
public class MyViewModel
{
  public string FundName { get; set; }
  public string FundType { get; set; }

  [NoMagic] // suppress transformation
  public int InternalId { get; set; }
}

So I have just developed a MSBuild task to do this magic after the build (http://kindofmagic.codeplex.com).

The question is, what kind of magical postprocessing would you like more?

Does automatic implementation of INotifyPropertyChanging makes sense?

Lex Lavnikov
  • 1,239
  • 9
  • 18
  • What about creating the same thing for INotifyPropertyChanging? – Matěj Zábský Dec 01 '10 at 17:57
  • @CommanderZ do you implement and use INotifyPropertyChanging? If so what for? perhaps u want to comment on http://stackoverflow.com/questions/3835788/other-than-linq-to-sql-does-anything-else-consume-inotifypropertychanging – Simon Dec 01 '10 at 23:02

5 Answers5

4

Try this

http://code.google.com/p/notifypropertyweaver/

  • No attributes required
  • No references required
  • No base class required

Here is my blog article about it

http://codesimonsays.blogspot.com/2010/11/attempting-to-solve-inotifypropertychan.html

It supports the attributes you request

  • NotifyPropertyAttribute (notify for a property)
  • NotifyForAllAttribute (notify for all properties on a type)
  • NotifyIgnoreAttribute (do not notify for a property or type)
  • AlsoNotifyFor (Allows the injection of notify code that points to a different property)

Although these are option and designed for fine tuning. Most injection is done by convention through analyzing the existing IL.

Simon
  • 33,714
  • 21
  • 133
  • 202
  • Probably because of alien project hosting ;) – Lex Lavnikov Dec 01 '10 at 23:12
  • Btw, like the idea with reference removal. I came to attribute removal only ;) – Lex Lavnikov Dec 01 '10 at 23:24
  • @Lex glad you like it. Is my approach similar to what you were planning? Any more feature you can think of? – Simon Dec 02 '10 at 05:15
  • @Lex I also support you having your own attributes. See "Have no reference in your project file" http://code.google.com/p/notifypropertyweaver/wiki/WeavingWithoutAddingAReference – Simon Dec 02 '10 at 05:16
1

"Magic" is almost always a terrible name for method or property or variable in any language. You should rename the attribute to something more descriptive. Imagine you are just a random internet pedestrian and stumble on a piece code with attribute "Magic", what does it tell you about the code? Exactly nothing :)

I will try your code anyways, it has potential to be quite a timesaver. This should definitely be a part of .NET.

Matěj Zábský
  • 16,909
  • 15
  • 69
  • 114
1

If we're going to have fancy code generation, I think I would prefer a way to generate DependancyProperties more easily. The snippit I use is certainly helpful, but I'm not a fan how jumbled the code looks when you have on-changed and coerce callbacks, and metadata options. Maybe I'll try and mock up a sample after work.

Edit: Well, here's one concept. It would look a lot more clever if you pass anonymous methods to attributes, but it's still a step up.

Before:

[DpDefault("The Void")]
[DpCoerce(new CoerceValueCallback(MainWindow.CoerceAddress))]
[DpChanged(new PropertyChangedCallback(MainWindow.ChangeAddress1))]
[DpChanged(new PropertyChangedCallback(MainWindow.ChangeAddress2))]
[DpOptions(FrameworkPropertyMetadataOptions.Inherits)]
public string Address {
    get { return Dp.Get<string>(); }
    set {
        if (Dp.Get<string>() != value) {
            Dp.Set(value);
            PostOffice.SendMailToTheBoss("I moved!");
        }
    }
}

After:

public string Address {
    get { return (string)GetValue(AddressProperty); }
    set {
        if ((string)GetValue(AddressProperty) != value) {
            SetValue(AddressProperty, value);
            PostOffice.SendMailToTheBoss("I moved!");
        }
    }
}

public static readonly DependencyProperty AddressProperty =
    DependencyProperty.Register("Address", typeof(string), typeof(MainWindow),
        new FrameworkPropertyMetadata((string)"The Void",
            FrameworkPropertyMetadataOptions.Inherits,
            new PropertyChangedCallback(MainWindow.ChangeAddress1)
                + new PropertyChangedCallback(MainWindow.ChangeAddress2),
            new CoerceValueCallback(MainWindow.CoerceAddress)));

Typically, only the 'DpDefault' attribute would be used, but even if it doesn't make the code shorter, it certainly makes it clearer. Here would be a more typical example:

Before:

[DpDefault("The Void")]
public string Address { get; set; }

After:

public string Address {
    get { return (string)GetValue(AddressProperty); }
    set { SetValue(AddressProperty, value); }
}

public static readonly DependencyProperty AddressProperty =
    DependencyProperty.Register("Address", typeof(string), typeof(MainWindow),
        new UIPropertyMetadata((string)"The Void"));
YotaXP
  • 3,844
  • 1
  • 22
  • 24
  • Good idea. We may generate dependency property from normal one by extracting setter in OnDependencyPropertyChanged handler. And generate DependencyProperty static readonly field with the correctly derived name, if it doesn't exist. – Lex Lavnikov Dec 01 '10 at 18:16
  • Edited the answer with a mock-up concept of what the code transformation could look like. – YotaXP Dec 02 '10 at 01:14
0

Something that might make your life a little easier is this... (Ive picked it up from Caliburn Micro).

 public virtual void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property) {
            NotifyOfPropertyChange(property.GetMemberInfo().Name);
        }

This enables you to do the following..

NotifyOfProperyChange(() => this.PropertyName);

This will highlight any issues with the code at design time, rather than run time.

Caliburn Micro is an awesome little framework that you should take a look at, it removes so much of the wiring up involved with MVVM and Silverlight / WPF!

BenjaminPaul
  • 2,931
  • 19
  • 18
  • I've been using C.M as well. But, frankly, this is a lot of wasted CPU cycles just to get a CORRECT name of the property. Main reason behind KindOfMagic project is to make it as performant as possible and zero typing for users as possible. – Lex Lavnikov Dec 01 '10 at 17:26
0

This can already be done using a AOP (Aspect Oriented Programming) tool like PostSharp : http://www.richard-banks.org/2009/02/aspect-oriented-programming.html (using v1.x) http://www.sharpcrafters.com/solutions/ui#data-binding (using v2.0)

I used PostSharp to implement INPC in few projects and it worked out pretty well, the code is much more cleaner and maintainable (it adds a few seconds to compile time)

Catalin DICU
  • 4,610
  • 5
  • 34
  • 47
  • I have tried PostSharp for INPC a month ago, but generated IL code for setters was so bloat and ugly (i used Reflector). My opinion is that, generic AOP tools introduce too big overhead for this tiny particular problem. – Lex Lavnikov Dec 01 '10 at 17:36
  • Have you validated this opinion with any performance testing? In my experience the overhead introduced by AOP in this case is not perceivable. – Robert Rossney Dec 01 '10 at 19:45
  • 1
    re postsharp bloat it injects approx 150 lines of code into each property. – Simon Dec 01 '10 at 21:21
  • @Robert, I just need a proper check old-new value and raise event if different. I need neither Starship "Enterprise" nor Battlestar "Galactica". – Lex Lavnikov Dec 01 '10 at 23:20
  • 1
    @Simon, it could inject 10000 lines of code into each property and it wouldn't trouble me as long as the users didn't notice a performance difference. I don't spend a lot of (or, indeed, any) time poking around in my setters with Reflector, though, so maybe I'm doing something wrong. Or right. – Robert Rossney Dec 02 '10 at 20:07
  • @Rob I am not concerned about the performance implications. I am concerned about my assembly size being several times larger due to 150 lines of code for every set. – Simon Dec 02 '10 at 23:47
  • @Rob I guess it comes down to trade offs. Postsharp is a great tool but the value proposition of using it for property notification is diluted because of the issues mentioned above. – Simon Dec 02 '10 at 23:49