Set of classes for easy and powerful implementation / use of INotifyPropertyChanged and INotifyCollectionChanged based objects.
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 |
Visit NuGet site or enter the following command:
Install-Package MarcelJoachimKloubert.Notifiable.dll
The following example demonstrates how to implement a simple ViewModel object:
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!";
}
}
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.
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.