Thursday, December 30, 2010

notifypropertyweaver – {get;set;notify} in Silverlight

I had written about notifypropertyweaver in one of my earlier post. In this post, I will take a detailed look at notifypropertyweaver. I assume that you are already aware of various implementations of INotifyPropertyChanged. If not, then take a look at my posts labeled INotifyPropertyChanged.

So, what does notifypropertyweaver do? If your class implements INotifyPropertyChanged then you just need to have auto-implemented properties and notifypropertyweaver automagically ensures that on change of value of a property, PropertyChanged event is fired.

Let us take a look at an example. We have the following class:

TryToWeaveAllTypes
using System.ComponentModel;

namespace ForTestingNotifyPropertyWeaver
{
    public class Item1 : INotifyPropertyChanged
    {
        public double Price { get; set; }

        public int Quantity { get; set; }

        public double TotalCost { get { return Price * Quantity; } }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Download NotifyPropertyWeaverMsBuildTask.dll. Modify the csproj file to add the following lines :

<UsingTask
    TaskName="NotifyPropertyWeaverMsBuildTask.WeavingTask"
    AssemblyFile="$(SolutionDir)Dependencies\Build\NotifyPropertyWeaverMsBuildTask.dll" />
  <Target
    Name="AfterBuild">
    <WeavingTask
      TryToWeaveAllTypes="true"
      TargetPath="$(TargetPath)" />
  </Target>
TryToWeaveAllTypes="true"  ensures that all auto-implemented properties in classes which have implemented INotifyPropertyChanged will notify on change of value.
Reflected code
public double Price
    {
        [CompilerGenerated]
        get
        {
            return this.<Price>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            if (!object.Equals(this.<Price>k__BackingField, value))
            {
                this.<Price>k__BackingField = value;
                this.OnPropertyChanged("Price");
                this.OnPropertyChanged("TotalCost");
            }
        }
    }

    public int Quantity
    {
        [CompilerGenerated]
        get
        {
            return this.<Quantity>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            if (!object.Equals(this.<Quantity>k__BackingField, value))
            {
                this.<Quantity>k__BackingField = value;
                this.OnPropertyChanged("Quantity");
                this.OnPropertyChanged("TotalCost");
            }
        }
    }

    public double TotalCost
    {
        get
        {
            return (this.Price * this.Quantity);
        }
    }

The above code which we get by reflecting our assembly shows that all the properties including the calculated property TotalCost notifies on change of value.

Now let us consider a scenario where in we don’t want to notify all properties or to put it another way, we want to ignore certain properties when notifypropertyweaver is injecting notification code. We do this by adding a reference to NotifyPropertyWeaver.dll and decorating the property which we don’t want to be notified with the NotifyIgnoreAttribute.

Let us say we don’t want Price and TotalCost property to be notified. We first remove the WeavingTask option, TryToWeaveAllTypes="true" from the csproj file.

 

<UsingTask TaskName="NotifyPropertyWeaverMsBuildTask.WeavingTask"
             AssemblyFile="$(SolutionDir)Dependencies\Build\NotifyPropertyWeaverMsBuildTask.dll" />
  <Target
    Name="AfterBuild">
    <WeavingTask
      TargetPath="$(TargetPath)" />
  </Target>

Now, we will have to exclusively specify which properties will notify on change of value.

Notify only certain properties
public class Item2:INotifyPropertyChanged
    {       
        public double Price { get; set; }

        [NotifyProperty]
        public int Quantity { get; set; }

        [DoNotNotify]
        public double TotalCost { get { return Price * Quantity; } }
       
        public event PropertyChangedEventHandler PropertyChanged;
    }

By default, no properties will notify. Hence, we have decorated the property Quantity with  [NotifyProperty] attribute. On change of Quantity, its dependent properties will also be notified. But, we don’t want TotalCost (a dependent of Quantity) to notify and hence we decorate it with [DoNotNotify] attribute.

This is how the reflected code will now look:

Reflected code
     // Properties
public double Price
{
    [CompilerGenerated]
    get
    {
        return this.<Price>k__BackingField;
    }
    [CompilerGenerated]
    set
    {
        this.<Price>k__BackingField = value;
    }
}

public int Quantity
{
    [CompilerGenerated]
    get
    {
        return this.<Quantity>k__BackingField;
    }
    [CompilerGenerated]
    set
    {
        if (!object.Equals(this.<Quantity>k__BackingField, value))
        {
            this.<Quantity>k__BackingField = value;
            this.OnPropertyChanged("Quantity");
        }
    }
}

public double TotalCost
{
    get
    {
        return (this.Price * this.Quantity);
    }
}

Some of you now might say that you don’t want to reference NotifyPropertyWeaver.dll. In that case, you can implement these attributes in your own project. Take a look at the implementation here.

There are various other scenarios that notifypropertyweaver takes care of. Take a look at the project wiki page. Also, Simon Cropp who maintains the project is very helpful with all the queries and also quickly responds if there is any issue.

You can download the source code from here. It is a Silverlight class library. But, you can use notifypropertyweaver also for wp7, wpf and winforms.

No comments:

Post a Comment