fire multiple property names?

Mar 27, 2011 at 2:57 AM

Nice stuff, wouldn't have thought it is possible to inspect a ref parameters that way. Comes in handy because I use ref parameters already anyway for all the boilerplate (checking equality etc...).

But what about firing multiple property names? Think of the classical example, a class Person with properties LastName and FirstName, and a read-only property FullName that must be notified too when either LastName or FirstName change. Guess we'll have to take one of the old-fashioned approaches then anyway. :-/

 

 

Coordinator
Mar 27, 2011 at 7:13 PM

Hi,

Regarding the FullName = FirstName + LastName example, ActiveSharp does actually contain most of the necessary "smarts" to do that.  In particular, because ActiveSharp can look inside the implementation of property getters and setters, it can figure out which properties are computed from other ones.  But there is one bit missing, unfortunately.  It needs a bit more code to actually use that information to fire off additional notifications (e.g. to fire off the notification for FullName when either FirstName or LastName changes).

I do actually have an old copy of the code to do that.  There are three minor problems however:

  1. I haven't yet got round to integrating that old code into the ActiveSharp release. ;-)
  2. It doesn't (and probably never could) work properly when virtual methods are involved.  E.g. if FirstName + LastName is done inside the property getter, or inside some other method of the object, then it works fine; but if that operation is done in a virtual method somewhere the logic cannot reliably work things out.  (Since the method used is equivalent to static, compile-time, analysis of the code - even though it actually executes at runtime).
  3. It gets complicated when multiple objects are involved.  E.g say you have Order and OrderLine objects.  The Order has a TotalPrice property, which is computed as the sum of the prices of its child OrderLines.  If you change OrderLine.Price, do you expect a change notification to be fired for TotalPrice on the associated Order?  Currently, I think its just too messy to automate a solution to this problem.  Instead, I think that any automated solution would have to just limit itself to derived properties which are defined on the same object as their "source" properties.

Perhaps the best solution might be to have something which actually subscribes to the change events on the object, and fires additional change events. E.g. it listens to changes on the Person object, and when it detects a change to LastName (or firstname) it fires off an additional change notification for FullName.  Such an implementation has a couple of nice advantages:

(a) It can be kept completely separate from the property implementation.  (So you could implement the property setters however you like - either with or without ActiveSharp).

(b) It could be expressed in Lambda expressions, for human readability.  E.g. GetChangeHelper<Person>.OnChange(p => p.LastName, p => p.FirstName).FireAdditionalChange(p => p.FullName)

(c) If necessary, such lambda expressions could be generated (in some kind of code-gen/compile-time step) by ActiveSharp (or similar).  I.e. something that inspects the bodies of the property getters, as described above.  But, you could also add in your own, for the cases that are too tricky to auto-generate.

Anyway, that's where my thinking is at currently.   Haven't found time to do anything about it yet.  Although, since it could be written completely separately from ActiveSharp, its the kind of thing that anyone could write, and make available as a separate CodePlex project, and then ppl could use it with, or without, ActiveSharp.

John

Jan 11, 2012 at 11:02 AM

Hi,

I am facing the same problem.

I know the main goal is to avoid the strings to call the PropertyChanged event, but maybe as a workaround a "OnPropertyChanged" method could be implemented to temporary give that functionality.

Coordinator
Jan 12, 2012 at 6:25 AM

Would something like this suit you?  (You can do this right now, no need for any changes to ActiveSharp):

 

public class Customer: SomeBaseObjectThatImplementsINotifyPropertyChanged

{

    public Customer()

    {

           this.PropertyChanged += OnPropertyChanged;   // i.e. Customer listens to it's _own_ notifications

     }

 

     private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)

   {

        switch(args.PropertyName)

      {

            case "FirstName": base.NotifyPropertyChanged(this, "FullName"); break;

           // where base.NotifyPropertyChanged will actually just call PropertyChangeHelper.NotifyPropertyChanged, if you are using PropertyChangeHelper

          // (you'll need to make your own copy of PropertyChangeHelper, and make that method public.  Just copy it from here

         // http://activesharp.codeplex.com/SourceControl/changeset/view/4e6a262988de#ActiveSharp%2fPropertyMapping%2fPropertyChangeHelper.cs

 

           ....

       }

   }

}

 

NB: I have not tested this code.  ;-)

Jan 12, 2012 at 10:04 AM
Edited Jan 12, 2012 at 10:05 AM

Thanks for the advice. What I actually do is:

1) Make PropertyChangeHelper.NotifyPropertyChanged() public

2) Add to my base clase:

 

public class BaseClass : INotifyPropertyChanged
{
    #region Automatic PropertyChanged
 
    readonly PropertyChangeHelper _propertyChangeHelper = new PropertyChangeHelper();
 
    public event PropertyChangedEventHandler PropertyChanged
    {
        add { _propertyChangeHelper.Add(value); }
        remove { _propertyChangeHelper.Remove(value); }
    }

    protected void SetValue<T>(ref T field, T value)
    {
        _propertyChangeHelper.SetValue(this, ref field, value);
    }

    #endregion
 
    public void OnPropertyChanged(string propertyName)
    {
        _propertyChangeHelper.NotifyPropertyChanged(this, propertyName);
     }
}

Maybe another solution is to add a vector of "affected properties" to the PropertyChangedHelper.SetValue() method.