Skip to content

Set of classes for easy and powerful implementation of IPropertyChanged based objects.

License

Notifications You must be signed in to change notification settings

mkloubert/Notifiable.NET

Repository files navigation

Notifiable.NET

Set of classes for easy and powerful implementation / use of INotifyPropertyChanged and INotifyCollectionChanged based objects.

Branches

Name Targets on
master (current) C# 4.0
CSharp5 C# 5.0
Portable8 C# 4.0, .NET 4.5, Silverlight 5, Windows 8, Windows Phone 8.1 + 8 (Silverlight)
NetCore5 C# 6.0, .NET Core 5

Install

NuGet

Visit NuGet site or enter the following command:

Install-Package MarcelJoachimKloubert.Notifiable.dll

Notifiable objects

The following example demonstrates how to implement a simple ViewModel object:

Basic usage

using System;
using MarcelJoachimKloubert.ComponentModel;

class MyViewModel : NotifiableBase {
    public object Value1 {
        get { return this.Get(() => this.Value1); }
        
        set { this.Set(value, () => this.Value1); }
    }
}

class Program {
    static void Main() {
       var vm = new MyViewModel();
       vm.PropertyChanged += (sender, e) =>
           {
               Console.WriteLine("Property '{0}' has been changed.",
                                 e.PropertyName);
           };
           
       // this will invoke the 'PropertyChanged' event
       vm.Value1 = "Hello, world!";
       
       // this will NOT invoke the 'PropertyChanged' event
       // because the value has not been changed
       vm.Value1 = "Hello, world!";
    }
}

Auto notification

You can use the ReceiveNotificationFrom attribute if you want raise the PropertyChanged event for a property if the value of another property has been changed:

class MyViewModel : NotifiableBase {
    public string Name {
        get { return this.Get(() => this.Name); }
        
        set { this.Set(value, () => this.Name); }
    }
    
    [ReceiveNotificationFrom("Name")]
    public string UpperName {
        get {
            if (this.Name == null) {
                return null;
            }
        
            return this.Name.ToUpper();
        }
    }
}

If you change the value of Name property, the object will also raise the property changed event for the UpperName property.

Another way to do this is to use the ReceiveValueFrom attribute.

This makes it possible to invoke a method, field or property when a values has been changed:

class MyViewModel : NotifiableBase {
    private string _upperName;

    public string Name {
        get { return this.Get(() => this.Name); }
        
        set { this.Set(value, () => this.Name); }
    }
    
    [ReceiveNotificationFrom("Name")]
    public string UpperName {
        get { return this._upperName; }
    }
    
    [ReceiveValueFrom("Name")]
    protected void OnNameChanged(IReceiveValueFromArgs args) {
        var newUpperName = (string)args.NewValue;
        if (newUpperName != null) {
            newUpperName = newUpperName.ToUpper();
        }
        
        this._upperName = newUpperName;
    }
}

In that example the OnNameChanged() method is invoked when the value of Name has been changed.

After that a property changed event is raised for the UpperName property.

Work thread safe

The NotifiableBase class uses an IDictionary<string, object> instance to store the properties and theirs values.

By default a Dictionary<string, object> instance is created.

The problem is, that this class is NOT thread safe!

Now you have two options:

The first (and not recommed) one is to use the InvokeThreadSafe() method:

class MyViewModel : NotifiableBase {
    public string Name {
        get {
            return this.InvokeThreadSafe(func: (obj) => this.Get(() => this.Name));
        }
        
        set {
            this.InvokeThreadSafe(action: (obj, state) => this.Set(state, () => this.Name),
                                  actionState: value);
        }
    }
}

The method uses the object from SyncRoot property for the internal used lock statement.

The problem is that you have to do this for all notifiable properties!

The seond and BETTER WAY is to initialize an own instance of the property storage.

Overwrite the CreatePropertyStorage() method and return an instance of a thread safe dictionary, like ConcurrentDictionary (or an own one):

using System.Collections.Concurrent;
using System.Collections.Generic;
using MarcelJoachimKloubert.ComponentModel;

class MyViewModel : NotifiableBase {
    protected override IDictionary<string, object> CreatePropertyStorage() {
        return new ConcurrentDictionary<string, object>();
    }
    
    
    public string Name {
        get { return this.Get(() => this.Name); }
        
        set { this.Set(value, () => this.Name); }
    }
}

And this is something that is done once in the constructor of the NotifiableBase class.

About

Set of classes for easy and powerful implementation of IPropertyChanged based objects.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages