A while back I used PostSharp to create a set of aspects to make the use of INotifyPropertyChanged a lot easier. I had something like this:

[ChangeNotifier]
public class Person {
  [PropertyChanged] public string FName {get; set;}
  [PropertyChanged] public string LName {get;set;}
  [DependsOn("FName")] public string Name {get { return FName + " " + LName;}}
}

The result is that when the FName property changes, the event is also raised for the Name property.

After applying the [Field] attribute to both the FName and LName properties it has come to my understanding that DO already fires the INotifyPropertyChanged event and my [PropertyChanged] attribute is ignored. (The visual studio debugger no longer reaches my code.)

The way my [DependsOn] attribute worked was from within the code that handled the [PropertyChanged]. It looked for any property in the current instance and determined whether the attribute existed. I can probably find a way to get around this, but is there already an existing attribute hidden in the massive DO code base that achieves the same thing?

edit: I noticed something else. If I apply my [PropertyChanged] attribute to a property that's not marked as [Field] the event is no longer raised for that property either.

asked Mar 24 '11 at 11:44

jensen's gravatar image

jensen
399913

edited Mar 24 '11 at 11:54

I'm afraid that there is no such an attribute for dependencies.

As for PropertyChanged attribute — could you check whether the aspect is being processed by PostSharp or not?

(Mar 25 '11 at 04:33) Dmitri Maximov Dmitri%20Maximov's gravatar image

One Answer:

The PropertyChangedAttribute was processed by PostSharp. But I found a work-around.

My DependsOnAttribute looks like this:

[Serializable]
public sealed class DependsOnAttribute : Attribute
{
private readonly string[] _properties;

public DependsOnAttribute(params string[] properties)
{
    _properties = properties;
}

public IEnumerable<string> Properties
{
    [DebuggerStepThrough]
    get { return _properties; }
}
}

As you can see, all it does is collect a list of strings.

I modified my ChangeNotifierAttribute. I know that Entity implements PropertyChanged, so all I needed was the name of the function used to raise the event. I found this in another question on this support board.

[Serializable]
[MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.None)]
public sealed class ChangeNotifierAttribute : InstanceLevelAspect
{
    [ImportMember("NotifyPropertyChanged", IsRequired = true)] public Action<string> OnRaisePropertyChanged;
    private object _instance;

    public override void RuntimeInitializeInstance()
    {
        Type type = _instance.GetType();
        PropertyInfo[] properties = type.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            object[] attributes = property.GetCustomAttributes(typeof (DependsOnAttribute), true);
            foreach (object att in attributes)
            {
                if (att.GetType() == typeof (DependsOnAttribute))
                {
                    DependsOnAttribute doa = att as DependsOnAttribute;
                    if (doa != null)
                    {
                        Entity entity = _instance as Entity;
                        if (entity != null)
                        {
                            PropertyInfo info = property;
                            entity.PropertyChanged += (o, e) =>
                                                        {
                                                            if (doa.Properties.Contains(e.PropertyName))
                                                            {
                                                                OnRaisePropertyChanged.Invoke(info.Name);
                                                            }
                                                        };
                        }
                    }
                }
            }
        }

        base.RuntimeInitializeInstance();
    }

    public override object CreateInstance(AdviceArgs adviceArgs)
    {
        _instance = adviceArgs.Instance;
        return base.CreateInstance(adviceArgs);
    }
}

For some strangeness with PostSharp's InstanceLevelAspect functionality I needed to introduce the member, otherwise the overrides were not called.

Now I do no longer need the PropertyChangedAttribute, as Field already does this, the other aspects remain in place.

answered Mar 25 '11 at 05:55

jensen's gravatar image

jensen
399913

Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA