Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alter property setter logic programmatically

I need to add logic of a property setter.

For example, I have a property named "CurrentTab":

private WorkspaceViewModel _currentTab;
public WorkspaceViewModel CurrentTab
{
    get
    {
          return _currentTab;
    }
    set
    {
          _currentTab = value;
          OnPropertyChanged("CurrentTab");
    }
}

This is all good and works, but I want to be able to just define it like this:

public WorkspaceViewModel CurrentTab { get; set; }

So that the system automatically performs the OnPropertyChanged() function for the property name after the setter has run without me adding any specific code. How to identify which properties need to follow this logic is no problem, I just need to find a way how to actually do it.

I want to make this simpler because I'll be having quite a lot of those kind of properties and I'd like to keep it clean.

Is there a way? Any help is much appreciated!

like image 995
Rik De Peuter Avatar asked May 19 '26 20:05

Rik De Peuter


2 Answers

Take a look: Fody. There is an add-in for INotifyPropertyChange: github

It is manipulating IL code while building the solution.

You need only to add attribute to view model:

[ImplementPropertyChanged]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

When code gets compiled:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged("GivenNames");
                OnPropertyChanged("FullName");
            }
        }
    }

    string familyName;
    public string FamilyName
    {
        get { return familyName; }
        set 
        {
            if (value != familyName)
            {
                familyName = value;
                OnPropertyChanged("FamilyName");
                OnPropertyChanged("FullName");
            }
        }
    }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }

    public virtual void OnPropertyChanged(string propertyName)
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

This can be achieved using PostSharp, which is an Aspect Oriented Programming approach:

In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. AOP forms a basis for aspect-oriented software development.

You can implement this using an Aspect called InstanceLevelAspect:

/// <summary> 
/// Aspect that, when apply on a class, fully implements the interface  
/// <see cref="INotifyPropertyChanged"/> into that class, and overrides all properties to 
/// that they raise the event <see cref="INotifyPropertyChanged.PropertyChanged"/>. 
/// </summary> 
[Serializable] 
[IntroduceInterface(typeof(INotifyPropertyChanged),  
                     OverrideAction = InterfaceOverrideAction.Ignore)] 
[MulticastAttributeUsage(MulticastTargets.Class | MulticastTargets.Property,  
                          Inheritance = MulticastInheritance.Strict)] 
public sealed class NotifyPropertyChangedAttribute : InstanceLevelAspect,  
                                                     INotifyPropertyChanged 
{ 
    /// <summary> 
    /// Field bound at runtime to a delegate of the method OnPropertyChanged
    /// </summary> 
    [ImportMember("OnPropertyChanged", IsRequired = false)]
    public Action<string> OnPropertyChangedMethod; 

    /// <summary> 
    /// Method introduced in the target type (unless it is already present); 
    /// raises the <see cref="PropertyChanged"/> event. 
    /// </summary> 
    /// <param name="propertyName">Name of the property.</param> 
    [IntroduceMember(Visibility = Visibility.Family, IsVirtual = true,  
                      OverrideAction = MemberOverrideAction.Ignore)] 
    public void OnPropertyChanged(string propertyName) 
    { 
        if (this.PropertyChanged != null) 
        { 
            this.PropertyChanged(this.Instance,  
                                  new PropertyChangedEventArgs(propertyName)); 
        } 
    } 

    /// <summary> 
    /// Event introduced in the target type (unless it is already present); 
    /// raised whenever a property has changed. 
    /// </summary> 
    [IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)] 
    public event PropertyChangedEventHandler PropertyChanged; 

    /// <summary> 
    /// Method intercepting any call to a property setter. 
    /// </summary> 
    /// <param name="args">Aspect arguments.</param> 
    [OnLocationSetValueAdvice,  
     MulticastPointcut( Targets = MulticastTargets.Property,  
         Attributes = MulticastAttributes.Instance)] 
    public void OnPropertySet(LocationInterceptionArgs args) 
    { 
        // Don't go further if the new value is equal to the old one. 
        // (Possibly use object.Equals here). 
        if (args.Value == args.GetCurrentValue()) 
        {
           return; 
        }

        // Actually sets the value. 
        args.ProceedSetValue(); 

        // Invoke method OnPropertyChanged (our, the base one, or the overridden one). 
        this.OnPropertyChangedMethod.Invoke(args.Location.Name); 
    } 
} 

Then, use it on your property like this:

[NotifyPropertyChanged]
public WorkspaceViewModel CurrentTab { get; set; }

This attirubte can also be applied at the class level, if you want all your properties to implement NotifyPropertyChanged. More on the example can be found here

like image 20
Yuval Itzchakov Avatar answered May 22 '26 12:05

Yuval Itzchakov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!