Creating an INotifyPropertyChanged proxy to dispatch calls to UI thread - c#

I would like to create a dynamic proxy for binding WinForms controls to objects changed by a different (non-GUI) thread. Such a proxy would intercept the PropertyChanged event and dispatch it using the proper SynchronizationContext.
That way I could use a helper class to do the job, without having to implement the synchronization manually every time (if (control.InvokeRequired) etc.).
Is there a way to do that using LinFu, Castle or a similar library?
[Edit]
Data source is not necessarily a list. It can be any business object, e.g.:
interface IConnection : INotifyPropertyChanged
{
ConnectionStatus Status { get; }
}
I could create a wrapper which could do the job, and it would look something like this:
public class ConnectionWrapper : IConnection
{
private readonly SynchronizationContext _ctx;
private readonly IConnection _actual;
public ConnectionWrapper(IConnection actual)
{
_ctx = SynchronizationContext.Current;
_actual= actual;
_actual.PropertyChanged +=
new PropertyChangedEventHandler(actual_PropertyChanged);
}
// we have to do 2 things:
// 1. wrap each property manually
// 2. handle the source event and fire it on the GUI thread
private void PropertyChanged(object sender, PropertyChangedEvArgs e)
{
// we will send the same event args to the GUI thread
_ctx.Send(delegate { this.PropertyChanged(sender, e); }, null);
}
public ConnectionStatus Status
{ get { return _instance.Status; } }
public event PropertyChangedEventHandler PropertyChanged;
}
(there may be some errors in this code, I am making it up)
What I would like to do is to have a dynamic proxy (Reflection.Emit) one liner for this, e.g.
IConnection syncConnection
= new SyncPropertyChangedProxy<IConnection>(actualConnection);
and I wanted to know if something like this was possible using existing dynamic proxy implementations.
A more general question would be: How to intercept an event when creating a dynamic proxy? Intercepting (overriding) properties is explained well in all implementations.
[Edit2]
The reason (I think) I need a proxy is that the stack trace looks like this:
at PropertyManager.OnCurrentChanged(System.EventArgs e)
at BindToObject.PropValueChanged(object sender, EventArgs e)
at PropertyDescriptor.OnValueChanged(object component, EventArgs e)
at ReflectPropertyDescriptor.OnValueChanged(object component, EventArgs e)
at ReflectPropertyDescriptor.OnINotifyPropertyChanged(object component,
PropertyChangedEventArgs e)
at MyObject.OnPropertyChanged(string propertyName)
You can see that BindToObject.PropValueChanged does not pass the sender instance to the PropertyManager, and Reflector shows that sender object is not referenced anywhere. In other words, when the PropertyChanged event is triggered, component will use reflection to access the property of the original (bound) data source.
If I wrapped my object in a class containing only the event (as Sam proposed), such wrapper class would not contain any properties which could be accessed through Reflection.

Here's a class that will wrap a INotifyPropertyChanged, forward the PropertyChanged event through SynchronizationContext.Current, and forward the property.
This solution should work, but with some time it could be improved to use a lambda expression instead of a property name. That would allow getting rid the reflection, provide typed access to the property. The complication with this is you need to also get the expression tree from the lambda to pull out the property name so you can use it in the OnSourcePropertyChanged method. I saw a post about pulling a property name from a lambda expression tree but I couldn't find it just now.
To use this class, you'd want to change your binding like this:
Bindings.Add("TargetProperty", new SyncBindingWrapper<PropertyType>(source, "SourceProperty"), "Value");
And here's SyncBindingWrapper:
using System.ComponentModel;
using System.Reflection;
using System.Threading;
public class SyncBindingWrapper<T> : INotifyPropertyChanged
{
private readonly INotifyPropertyChanged _source;
private readonly PropertyInfo _property;
public event PropertyChangedEventHandler PropertyChanged;
public T Value
{
get
{
return (T)_property.GetValue(_source, null);
}
}
public SyncBindingWrapper(INotifyPropertyChanged source, string propertyName)
{
_source = source;
_property = source.GetType().GetProperty(propertyName);
source.PropertyChanged += OnSourcePropertyChanged;
}
private void OnSourcePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != _property.Name)
{
return;
}
PropertyChangedEventHandler propertyChanged = PropertyChanged;
if (propertyChanged == null)
{
return;
}
SynchronizationContext.Current.Send(state => propertyChanged(this, e), null);
}
}

I have come across the same problems and Samuel's solution didn't work for me, so I placed the synchronization context initialization in the constructor, and the "Value" property name should be passed instead of the original property. This worked for me:
public class SyncBindingWrapper: INotifyPropertyChanged
{
private readonly INotifyPropertyChanged _source;
private readonly PropertyInfo _property;
public event PropertyChangedEventHandler PropertyChanged;
private readonly SynchronizationContext _context;
public object Value
{
get
{
return _property.GetValue(_source, null);
}
}
public SyncBindingWrapper(INotifyPropertyChanged source, string propertyName)
{
_context = SynchronizationContext.Current;
_source = source;
_property = source.GetType().GetProperty(propertyName);
source.PropertyChanged += OnSourcePropertyChanged;
}
private void OnSourcePropertyChanged(object sender, PropertyChangedEventArgs e)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null && e.PropertyName == _property.Name)
{
_context.Send(state => propertyChanged(this, new PropertyChangedEventArgs("Value")), null);
}
}
}
Usage:
_textBox1.DataBindings.Add("Text", new SyncBindingWrapper(someObject, "SomeProperty"), "Value");

Without relying on the SynchrnoisationConext you can rely on ISynchronizeInvoke
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
foreach (EventHandler h in handler.GetInvocationList())
{
var synch = h.Target as ISynchronizeInvoke;
if (synch != null && synch.InvokeRequired)
synch.Invoke(h, new object[] { this, e });
else
h(this, e);
}
}
}

Related

A nicer way to handle Properties Changes in C#

I'm building a MVVM application in which a ToBeListened class has a couple of properties, PropertyA and PropertyB, and I want to listen to them.
public class ToBeListened : INotifyPropertyChanged
{
private double _propertyA;
private string _propertyB;
/*Here I'm omitting the update part where NotifyPropertyChanged gets called*/
public double PropertyA{get; set; }
public double PropertyB{get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Those two properties are listened by a Listener class, so I've implemented an EventHandler in it, that listens to a ToBeListened object.
public class Listener
{
private ToBeListened toBeListenedObject;
public Listener()
{
toBeListenedObject = new ToBeListened();
toBeListenedObject.PropertyChanged += newPropertyChangedEventHandler(PropertyListener_PropertyChanged);
}
private void PropertyListener_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch(e.PropertyName)
{
case "PropertyA":
{
/*...DO SOMETHING...*/
}
case "PropertyB":
{
/*...Do something else...*/
}
}
The thing is, I don't really like this solution I've found. A switch-case isn't polymorphism-friendly, so
is there a better way to do this? Maybe something that uses overloading? (Like private void PropertyListener_PropertyChanged(double sender, PropertyChangedEventArgs e)
most of all, is it right to code a ViewModel like this?
I like Josh Smith's PropertyObserver, which you can get at http://mvvmfoundation.codeplex.com/ (some documentation from Josh at https://joshsmithonwpf.wordpress.com/2009/07/11/one-way-to-avoid-messy-propertychanged-event-handling/). It's a nice class that encapsulates the plumbing logic you're talking about, so you can focus on just handling changes to certain properties. So in your case, you could write code like:
var observer = new PropertyObserver<ToBeListened>(toBeListenedObject)
.RegisterHandler(tbl => tbl.PropertyA, tbl => HandlePropertyA(tbl))
.RegisterHandler(tbl => tbl.PropertyB, tbl => HandlePropertyB(tbl));
You can start using it by installing the MVVM Foundation nuget package into your solution. The ID is MvvmFoundation.Wpf.
In the past I've used a little class derived from Dictionary<string, Action> for this purpose. It was something like this:
public class PropertyChangedHandler : Dictionary<string, Action>
{
public PropertyChangedHandler(INotifyPropertyChanged source)
{
source.PropertyChanged += Source_PropertyChanged;
}
private void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Action toDo;
if (TryGetValue(e.PropertyName, out toDo))
{
toDo();
}
}
}
Then your listener looks like this:
public class Listener
{
private ToBeListened toBeListenedObject = new ToBeListened();
PropertyChangedHandler handler;
public Listener()
{
handler = new PropertyChangedHandler(toBeListenedObject)
{
{ "PropertyA", DoA },
{ "PropertyB", DoB }
};
}
private void DoB()
{
}
private void DoA()
{
}
}
This is just an example to give you an idea - it can be expanded for more complex purposes, of course.
i think MVVM Light framework (or library?) has what you need. Take a look at their ObservableObject class http://www.mvvmlight.net/help/SL5/html/d457231f-6af7-601d-fa1f-1fe7c9f60c57.htm
Basically what it does, is making your object observable.

Why is my event handler null?

I've got quite simple problem but gives me a headache and I'm wasting too much time. I've ran out of ideas anyway:)
For some reason I can't pass the value of my property variable to my event handler. Here is what I've got , an for me everything is fine but it won't work:(
Any idea why it's not passing the actual value of the variable? Thanks in advance:)
Just as the name suggests, propertyName should contain the property's name, not value. Also see PropertyChangedEventHandler and PropertyChangedEventArgs on MSDN for more details.
As to why your event handler null, I suspect you haven't subscribed to it. You should have something like the following somewhere in your program:
obj.PropertyChanged += new PropertyChangedEventHandler(obj_PropertyChanged);
private void obj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
....
}
then when obj.Moves changes obj_PropertyChanged will be called.
I understand your confusion, so let me give a little more explanations. Suppose you have two classes A and B, and you want B to be notified when a property of A is changed. Then you can make A implement INotifyPropertyChanged, and make B subscribe to the PropertyChanged event of A, like the following:
public class A: INotifyPropertyChanged
{
private int moves;
public int Moves
{
get { return moves; }
set
{
if (value != moves) {
moves = value;
OnPropertyChanged("Moves");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class B
{
private A a = new A();
public B()
{
a.PropertyChanged += new PropertyChangedEventHandler(a_PropertyChanged);
}
private void a_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
....
}
}
With the above, B.a_PropertyChanged will be called whenever A.Moves is changed. More precisely, if b is an instance of B, then b.a_PropertyChanged will be called whenever b.a.Moves is changed. Also please note how my implementation of the setter of Moves differs from yours.

Get notified when static property changes in wpf

Here microsoft described that in wpf 4.5 we can use INotifypropertyChanged for static properties as well. So I tried to do that.
Here is the code:
public static event PropertyChangedEventHandler StaticPropertyChanged;
protected static void OnStaticPropertyChanged(string PropertyName)
{
PropertyChangedEventHandler handler = StaticPropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(PropertyName));
}
}
But I dont know what to use instead of this keyword in the above code?
Here is my code:
public static event PropertyChangedEventHandler StaticPropertyChanged;
protected static void OnStaticPropertyChanged(string PropertyName)
{
PropertyChangedEventHandler handler = StaticPropertyChanged;
if (handler != null)
{
handler(typeof(MainWindowViewModel), new PropertyChangedEventArgs(PropertyName));
}
}
private static Haemogram _cHaemogram;
public static Haemogram cHaemogram
{
get
{
return _cHaemogram;
}
set
{
_cHaemogram = value;
OnStaticPropertyChanged("cHaemogram");
}
}
Unless anything uses the sender parameter, it won't matter. Logically it would make sense to use the type:
handler(typeof(TypeDeclaringEvent), new PropertyChangedEventArgs(PropertyName));
EDIT: Note that in the document you referred to, it states:
The static event can use either of the following signatures.
public static event EventHandler MyPropertyChanged;
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
Your event doesn't comply with these, which could be an issue.
Think that you added this to your viewmodel :
yourClass.StaticPropertyChanged+= yourClassStaticPropertyChanged;
...
void yourClassStaticPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
}
The "this" keyword refers the "object sender" parameter.
If you use "this" in your code while creating handler, it refers "sender" in yourClassStaticPropertyChanged function. If you send null, the sender parameter will be null.
--Edit--
If you want to get changes to the textbox add this code to your viewmodel :
private string _updatedText;
public string UpdatedText
{
get
{
return _updatedText;
}
set
{
_updatedText= value;
OnStaticPropertyChanged("UpdatedText")
}
}
And set UpdatedText in the event :
void yourClassStaticPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UpdatedText=e.NewValue;
}
then bind the UpdatedText to your textbox like this :
<TextBlock Text="{Binding UpdatedText}"/>

call converter in XAML if property of a object in the observable collection changes

I have a converter that accepts an ObservableCollection as a parameter, and I'd like to re-evaluate it whenever a specific property on any item in the collection changes
For example: lets say I have bound a label to a collection of Person objects with a converter. The job of the converter is to count the number of Persons in the list that are female, and return "valid" for 1 female or "accepted" for 2. I'd like the converter to get called again anytime the Gender property on any Person object gets changed.
How can I accomplish this?
That's a classic problem you end up having if you play around WPF long enough.
I've tried various solutions, but the one that works best is to use a BindingList like so:
public class WorldViewModel : INotifyPropertyChanged
{
private BindingList<Person> m_People;
public BindingList<Person> People
{
get { return m_People; }
set
{
if(value != m_People)
{
m_People = value;
if(m_People != null)
{
m_People.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnPeopleListChanged(this);
};
}
RaisePropertyChanged("People");
}
}
}
private static void OnPeopleListChanged(WorldViewModel vm)
{
vm.RaisePropertyChanged("People");
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(String prop)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
}
Then just bind to the People collection like you would do with an ObservableCollection, except bindings will be re-evaluated when any property in its items change.
Also, please note that OnPeopleListChanged is static, so no memory leaks.
And Person should implement INotifyPropertyChanged.
A CollectionChanged event is only thrown when an item is added or removed from the collection (not when an item in the collection is changed). So the converter is not called when an item is changed.
One option:
In the Gender Property Set include logic to evaluate the collection and set a string Property that you bind the label to.
Wrote generic version of the answer from Baboon
public class ObservalbeList<T>: INotifyPropertyChanged
{
private BindingList<T> ts = new BindingList<T>();
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged( String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public BindingList<T> Ts
{
get { return ts; }
set
{
if (value != ts)
{
Ts = value;
if (Ts != null)
{
ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnListChanged(this);
};
}
NotifyPropertyChanged("Ts");
}
}
}
private static void OnListChanged(ObservalbeList<T> vm)
{
vm.NotifyPropertyChanged("Ts");
}
public ObservalbeList()
{
ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
{
OnListChanged(this);
};
}
}

array of events in C#?

basically:
public delegate void RecvCommandHandler (ChatApplication sender, byte[] content);
event RecvCommandHandler[] commands = new RecvCommandHandler[255];
I want to activate a different method/function for each command number, but I am really uncertain of the syntax. How am I supposed to do it?
I think I'll go with just an array of delegates for this one, but the question is still interesting.
You could create an array of a class with operator overloading to simulate the behavior you are interested in...
public delegate void EventDelegate(EventData kEvent);
public class EventElement
{
protected event EventDelegate eventdelegate;
public void Dispatch(EventData kEvent)
{
if (eventdelegate != null)
{
eventdelegate(kEvent);
}
}
public static EventElement operator +(EventElement kElement, EventDelegate kDelegate)
{
kElement.eventdelegate += kDelegate;
return kElement;
}
public static EventElement operator -(EventElement kElement, EventDelegate kDelegate)
{
kElement.eventdelegate -= kDelegate;
return kElement;
}
}
public EventElement[] commands = new EventElement[255];
commands[100] += OnWhatever;
commands[100].Dispatch(new EventData());
commands[100] -= OnWhatever;
There's really no concept of an array of events - it's like talking about an array of properties. Events are really just methods which let you subscribe and unsubscribe handlers. If you need to be able to do this by index, I suggest you just have a pair of methods. (AddCommandHandler(int, RecvCommandHandler) and RemoveCommandHandler(int, RecvCommandHandler)). That won't support the normal event handling syntactic sugar, of course, but I don't see that there's a lot of alternative.
The other option is to specify and index in the delegate prototype and have one event handler that "delegates" to the others, e.g.:
public delegate void RecvCommandHandler (int id, ChatApplication sender, byte[] content);
// ...
private RecvCommandHandler[] internalhandlers;
public void MyCommandHandler(int id, ChatApplication sender, byte[] content)
{
internalHandlers[id](id, sender, content);
}
I was just looking for the same answer, however my class is also event sender for WPF, so it should look as much as normal C#/WPF event sender class. So I simply added this:
To sender:
enum with properties name -- this is lame workaround for lack of nameof
one additional method to record requests
To receiver:
request event for given enum
The code, sender:
public enum Properties
{
NetworkFileName,
DatasetFileName,
LearningWatch
}
private string network_filename;
public string NetworkFileName
{
get { return network_filename; }
private set
{
if (network_filename != value)
{
network_filename = value;
OnPropertyChanged(Properties.NetworkFileName.ToString());
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public void OnChange(Properties prop, Action<object, PropertyChangedEventArgs> action)
{
PropertyChanged += new PropertyChangedEventHandler((obj, args) => { if (args.PropertyName == prop.ToString()) action(obj, args); });
}
And to the receiver:
private void OnNetworkLoaded(object sender, PropertyChangedEventArgs e)
{
SetTitle();
}
...
ExpManager.OnChange(ExperimentManager.Properties.DatasetFileName, OnDatasetLoaded);
It is still ugly, but at least:
I don't have to deal with "ifs" in receiver
I can easily create multiple event handlers
it is compatible with WPF
no magic strings (I hate those)
Disadvantage:
obsfuscation ruins this (but I have special class for that case, this project is just for me, so no problem here)

Categories