PostSharp NotifyPropertyChanged Model - PropertyChangedEventHandler - wpf

I have litle problem with PostSharp Implementation of INotifyPropertyChanged. PostSharp added PropertyChangedEventHandler PropertyChanged after compile time, but I need react from C# too.
Model a = new Model();
a.PropertyChanged += a_PropertyChanged;
Model implementation;
[NotifyPropertyChanged]
internal class Model
{
public string A { get; set; }
public string B { get; set; }
public string C { get { return string.Format("{0} - {1}", A, B); } }
}
I tried different ways to add handler,but unsuccessfully. Is there some way to do this?

Instance of class decorated by NotifyPropertyChanged can be casted to INotifyPropertyChanged at runtime:
((INotifyPropertyChanged)a).PropertyChanged
There is a helper method Post.Cast to avoid "Suspicious cast" warning:
Post.Cast<Model, INotifyPropertyChanged>(a).PropertyChanged += OnPropertyChanged;

Related

PropertyChanged for an extended class

My VS2015 solution consists of two projects: DataModel and DesktopClient.
DataModel has a Customer class - thats an EntityFramework 6 DB entity. Customer has a FirstName property.
In DesktopClient there is an extended class CustomerExt.
In DesktopClient, is it possible to have a notification to CustomerExt.FirstName changes? Defining a partial Customer across two projects won't work - DataModel is compiled first and it won't have partial properties defined in DesktopClient.
public class CustomerExt : Customer, INotifyPropertyChanged
{
public object Clone()
{
return this.MemberwiseClone();
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
this._isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
Unfortunately, if your base class does not implement INotifyPropertyChanged the safest way is to just write a wrapper class and only use that in your software. You can fit this in with your CustExt, or make it separate if you feel you want the extra layer.
This also assumes that while you may not control the Customer class, you control all of the code creating/editing the Customer instances, so that you can use this new class instead, then convert it to the original Customer class only when needed (such as a database transaction).
public class CustomerExt: INotifyPropertyChanged
{
Customer _customer = new Customer();
public object Clone()
{
return this.MemberwiseClone();
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
this._isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
#region WrapperProperties
public bool FirstName
{
get { return _customer.FirstName; }
set
{
_customer.FirstName= value;
NotifyPropertyChanged("FirstName");
}
}
#endregion
public Customer ToCustomer()
{
// returning a copy of the _customer instance here is safer than returning
// the reference, otherwise the properties could be altered directly
}
#region INotifyPropertyChanged
...
}
Some of this gets a little easier if you have an ICustomer interface and that is used during the database calls, then you can skip the formality of retaining a Customer instance.
I remember there being some third party libraries that have tried to automate this process - but I have never tried them and/or didn't trust them to work properly.
Let me see if I understand, you want update the View when your date is updated on the database?
You have to find a way to request this information from your ViewModel.
some kind of RefreshFirstNameAsync
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
this._firstName= value;
NotifyPropertyChanged("FirstName"); // There is better ways to implement that line
}
}
private void RefreshFirstName(){
FirstName = _userRepo.GetFirstNameAsync();
}

Raising OnPropertyChanged in the setter of each property vs Instance of Object

Information for the question:
I am trying to understand how to properly implement INotifyPropertyChanged on objects and collections.
First, here is my ViewModelBase class:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertychanged([CallerMemberName] string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Consider that I have a class called Person:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Age { get; set; }
}
To use INotifyPropertyChanged, most examples that I have seen change the Person class to something like this:
public class Person
{
public int Id { get; set; }
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertychanged();
}
}
private string _age;
public string Age
{
get { return _age; }
set
{
_age = value;
OnPropertychanged();
}
}
}
It seems to work exactly the same when used a single time on an instance of the object (This might be useful if there are a lot of properties):
private Person _person;
public Person MyPerson
{
get { return _person; }
set
{
_person = value;
OnPropertychanged();
}
}
Actual question:
1 - Does it make a difference (aside from amounts of code) whether you call OnPropertychanged() on each individual property verses on an instance of an object? (Are both considered good practice?)
2 - If setting OnPropertychanged() on the object instance is good practice, am I correct to create an ObservableCollection like this?:
var PersonCollection = new ObservableCollection<MyPerson>();
1) Well, if you want to call it on object instance, then you need to do it every time you use your class like this in binding. When you implement OnNotifyPropertyChanged directly inside your class, you don't need to care about it later on...
2) Classes with INotifyPropertyChanged do not require Observable collections. This is however must when you are binding colection do some UI control (ListBox, ListView) and want to add/remove its elements. Observable collection will then make sure the UI gets updated.
The ObservableCollections object... When adding and removing from this collection the UI will be notified of the changes (Top Level). If you have an "ObservableCollection of Person" and you change a property on the one of the objects(Person) in the list the UI will not update unless your "Person" class implements the INotifyPropertyChanged interface, which can be put into a base class that all classes can inherit from like your example. I hope this helps a little.

Castle Windsor proxies, implicit interfaces and WPF Binding

I am attempting to implement a WPF ViewModel using Castle Windsor Dynamic Proxies. The idea is that I want to supply an interface (IPerson below should suffice as an example), a concrete backing class, and an interceptor (for providing automatic implementation of INotifyPropertyChanged). The interceptor implementation is here: http://www.hightech.ir/SeeSharp/Best-Implementation-Of-INotifyPropertyChange-Ever
The problem that I am seeing is that when I bind my models to WPF controls, the controls don't see the models as implementing INotifyPropertyChanged. I believe (but am not sure) that this is because Windsor is implementing the interfaces explicitly, and WPF seems to expect them to be implicit.
Is there any way to make this work, so that changes to the model are caught by the interceptor and raised to the model?
All versions of the libraries are the latest: Castle.Core 2.5.1.0 and Windsor 2.5.1.0
Code is as follows:
// My model's interface
public interface IPerson : INotifyPropertyChanged
{
string First { get; set; }
string LastName { get; set; }
DateTime Birthdate { get; set; }
}
// My concrete class:
[Interceptor(typeof(NotifyPropertyChangedInterceptor))]
class Person : IPerson
{
public event PropertyChangedEventHandler PropertyChanged = (s,e)=> { };
public string First { get; set; }
public string LastName { get; set; }
public DateTime Birthdate { get; set; }
}
// My windsor installer
public class Installer : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<NotifyPropertyChangedInterceptor>()
.ImplementedBy<NotifyPropertyChangedInterceptor>()
.LifeStyle.Transient);
container.Register(
Component.For<IPerson, INotifyPropertyChanged>()
.ImplementedBy<Person>().LifeStyle.Transient);
}
}
So the answer turned out to be fairly straightforward... The code from http://www.hightech.ir/SeeSharp/Best-Implementation-Of-INotifyPropertyChange-Ever defines the interceptor as:
public class NotifyPropertyChangedInterceptor : IInterceptor
{
private PropertyChangedEventHandler _subscribers = delegate { };
public void Intercept(IInvocation invocation)
{
if (invocation.Method.DeclaringType == typeof(INotifyPropertyChanged))
{
HandleSubscription(invocation);
return;
}
invocation.Proceed();
if (invocation.Method.Name.StartsWith("set_"))
{
FireNotificationChanged(invocation);
}
}
private void HandleSubscription(IInvocation invocation)
{
var handler = (PropertyChangedEventHandler)invocation.Arguments[0];
if (invocation.Method.Name.StartsWith("add_"))
{
_subscribers += handler;
}
else
{
_subscribers -= handler;
}
}
private void FireNotificationChanged(IInvocation invocation)
{
var propertyName = invocation.Method.Name.Substring(4);
_subscribers(invocation.InvocationTarget, new PropertyChangedEventArgs(propertyName));
}
}
In my case, the InvocationTarget was simply not the right entity to be passing as the first argument to PropertyChanged (because I am generating a proxy). Changing the last function to the following fixed the problem:
private void FireNotificationChanged(IInvocation invocation)
{
var propertyName = invocation.Method.Name.Substring(4);
_subscribers(invocation.Proxy, new PropertyChangedEventArgs(propertyName));
}
I think you need to make the members of your Class that implements the interface Virtual.

Implementing INotifyCollectionChanged interface

I need to implement a collection with special capabilities. In addition, I want to bind this collection to a ListView, Therefore I ended up with the next code (I omitted some methods to make it shorter here in the forum):
public class myCollection<T> : INotifyCollectionChanged
{
private Collection<T> collection = new Collection<T>();
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void Add(T item)
{
collection.Insert(collection.Count, item);
OnCollectionChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
protected virtual void OnCollectionChange(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
CollectionChanged(this, e);
}
}
I wanted to test it with a simple data class:
public class Person
{
public string GivenName { get; set; }
public string SurName { get; set; }
}
So I created an instance of myCollection class as follows:
myCollection<Person> _PersonCollection = new myCollection<Person>();
public myCollection<Person> PersonCollection
{ get { return _PersonCollection; } }
The problem is that the ListView does not update when the collection updates although I implemented the INotifyCollectionChanged interface.
I know that my binding is fine (in XAML) because when I use the ObservableCollecion class instead of myCollecion class like this:
ObservableCollection<Person> _PersonCollection = new ObservableCollection<Person>();
public ObservableCollection<Person> PersonCollection
{ get { return _PersonCollection; } }
the ListView updates
What is the problem?
In order for your collection to be consumed, you should implement IEnumerable and IEnumerator too. Although, you're probably better off subclassing ObservableCollection<T>

Simple small INotifyPropertyChanged implementation

Say I have the following class:
public MainFormViewModel
{
public String StatusText {get; set;}
}
What is the easiest smallest way to get my changes to StatusText to reflect to any controls that bind to it?
Obviously I need to use INotifyPropertyChanged, but is there a cool way to do it that does not clutter up my code? need lots of files? etc?
Note: If this is a dupe then I am sorry. I searched and could not find any thing but using T4 code Generation which does not sound easy (to setup at least).
Unfortunately C# doesn't offer an easy mechanism to do that automatically... It has been suggested to create a new syntax like this :
public observable int Foo { get; set; }
But I doubt it will ever be included in the language...
A possible solution would to use an AOP framework like Postsharp, that way you just need to decorate your properties with an attribute:
public MainFormViewModel : INotifyPropertyChanged
{
[NotifyPropertyChanged]
public String StatusText {get; set;}
}
(haven't tried, but I'm pretty sure Postsharp allows you to do that kind of thing...)
UPDATE: OK, I managed to make it work. Note that it's a very crude implementation, using reflection on a private field to retrieve the delegate... It could certainly be improved, but I'll leave it to you ;)
[Serializable]
public class NotifyPropertyChangedAttribute : LocationInterceptionAspect
{
public override void OnSetValue(LocationInterceptionArgs args)
{
object oldValue = args.GetCurrentValue();
object newValue = args.Value;
base.OnSetValue(args);
if (args.Instance is INotifyPropertyChanged)
{
if (!Equals(oldValue, newValue))
{
RaisePropertyChanged(args.Instance, args.LocationName);
}
}
}
private void RaisePropertyChanged(object instance, string propertyName)
{
PropertyChangedEventHandler handler = GetPropertyChangedHandler(instance);
if (handler != null)
handler(instance, new PropertyChangedEventArgs(propertyName));
}
private PropertyChangedEventHandler GetPropertyChangedHandler(object instance)
{
Type type = instance.GetType().GetEvent("PropertyChanged").DeclaringType;
FieldInfo propertyChanged = type.GetField("PropertyChanged",
BindingFlags.Instance | BindingFlags.NonPublic);
if (propertyChanged != null)
return propertyChanged.GetValue(instance) as PropertyChangedEventHandler;
return null;
}
}
Note that your class still need to implement the INotifyPropertyChanged interface. You just don't have to explicitly raise the event in your property setters.
Have a go of this http://code.google.com/p/notifypropertyweaver/
All you need to do is implement INotifyPropertyChanged
So your code will look like
public MainFormViewModel : INotifyPropertyChanged
{
public String StatusText {get; set;}
#region INotifyPropertyChanged Implementation
}
The build task will compile this (you never see the below code)
public MainFormViewModel : INotifyPropertyChanged
{
public String StatusText {get; set;}
private string statusText;
public string StatusText
{
get { return statusText; }
set
{
if (value!= statusText)
{
statusText = value;
OnPropertyChanged("StatusText");
}
}
}
#region INotifyPropertyChanged Implementation
}
By leveraging EqualityComparer.Default you can reduce the property setter code down to one line as follows:
private int unitsInStock;
public int UnitsInStock
{
get { return unitsInStock; }
set { SetProperty(ref unitsInStock, value, "UnitsInStock"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T field, T value, string name)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
If your view models inherit from a base class that defines the SetProperty method and the PropertyChanged event, then the amount of code required to support INotifyPropertyChanged in your child view models becomes very minimal (1 line).
This approach is more verbose then the code weaving methods mentioned in other answers, but doesn't require you to modify your build process to accomplish it.
Be sure to take a look at the upcoming C# 5 Caller Info attributes as well as it looks like they will allow us to avoid using a magic string in the method without the performance cost of reflection.
UPDATE (March 1st, 2012):
The .NET 4.5 Beta is out, and with it, you can further refine the above code to this which removes the need for the string literal in the caller:
private int unitsInStock;
public int UnitsInStock
{
get { return unitsInStock; }
set
{
SetProperty(ref unitsInStock, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I have a blog post that talks about it in slightly more detail.
Ive always liked this method
private string m_myString;
public string MyString
{
get { return m_myString; }
set
{
if (m_myString != value)
{
m_myString = value;
NotifyPropertyChanged("MyString");
}
}
}
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
or for less code bloat
set
{
m_myString = value;
NotifyPropertyChanged("MyString");
}
I have a base class called "Model". It exposes a protected object called DataPoints, which is essentially a dictionary.
C#
public String StatusText {
get {
return (string)DataPoints["StatusText"];
}
set {
DataPoints["StatusText"] = value;
}
}
VB
public Property StatusText as String
get
return DataPoints!StatusText
end get
set
DataPoints!StatusText = value
end set
end property
When you set a value in the DataPoints dictionary it does the following:
Checks to make sure the value actually changed.
Saves the new value
Sets the IsDirty property to true.
Raises the Property Changed event for the named property as well as the IsDirty and IsValid properties.
Since it is a dictionary, it also makes loading objects from a database or XML file really easy.
Now you may think reading and writing to dictionary is expensive, but I've been doing a lot of performance testing and I haven't found any noticable impact from this in my WPF applications.
The PropertyChanged.Fody NuGet package does this.
https://github.com/Fody/PropertyChanged
Add the PropertyChanged.Fody package to your project.
Reference PropertyChanged in your model: using PropertyChanged;
Add the [ImplementPropertyChanged] attribute to your class.
All of the properties in the class will now magically implement INotifyPropertyChanged. Note - Fody works by modifying the emitted IL so you will never actually see the code in VS - it just magically does it.
Additional docs:
https://github.com/Fody/PropertyChanged/wiki/Attributes

Resources