Declare EventHandlers as Static or Non-Static in WPF Controls
I was working on creating a custom control with Command behavior and came across something odd. Some articles I found declared the CanExecuteChangedHandler EventHandler as static and others were non-static. Microsoft's SDK documentation shows static but when I declare it as static I get odd behavior when using multiple controls.
private static EventHandler canExecuteChangedHandler;
开发者_StackOverflowprivate void AddSecureCommand(ISecureCommand secureCommand)
{
canExecuteChangedHandler = new EventHandler(CanExecuteChanged);
securityTypeChangedHandler = new EventHandler(SecurityTypeChanged);
if (secureCommand != null)
{
secureCommand.CanExecuteChanged += canExecuteChangedHandler;
secureCommand.SecurityTypeChanged += securityTypeChangedHandler;
}
}
Does anyone know the proper way? Am I doing something wrong that is causing the static EventHandler not to work?
The stated reason for keeping a local copy of EventHandler
is that the WPF commanding sub-system uses weak references internally and therefore we need to keep a reference to the specific delegate object that is added to the CanExecuteChanged
event. If fact, anytime we are adding to any commanding sub-system event, we should also observe this practice, as you have for SecurityTypeChanged
.
The short answer to your question is that canExecuteChangedHandler
can be static, but you must be careful to only initialize it once. The reason it can be static is that all new EventHandler(CanExecuteChanged)
will do the same thing if CanExecuteChanged
is static. The reason to initialize it once is that different instances are different.
A private property that has the right read-only semantics is:
static EventHandler canExecuteChangedHandler
{
get
{
if (internalCanExecuteChangedHandler == null)
internalCanExecuteChangedHandler = new EventHandler(CanExecuteChanged);
return internalCanExecuteChangedHandler;
}
}
static EventHandler internalCanExecuteChangedHandler;
but this only works if CanExecuteChanged
is static. If it is not, then remove the static
qualifiers. In either case you have to be careful to actually use the property.
In this particular example, the second time that AddSecureCommand
is called the first canExecuteChangedHandler
is at risk of being garbage collected.
Finally, if this all sounds like black-magic, here is a code example to show what is happening.
public class Container
{
private WeakReference reference;
public object Object
{
get { return reference.IsAlive ? reference.Target : null; }
set { reference = new WeakReference(value); }
}
}
public class DelegateTest
{
private EventHandler eventHandler;
private Container container1;
private Container container2;
void MyEventHandler(object sender, EventArgs args)
{
}
public DelegateTest()
{
this.eventHandler = new EventHandler(MyEventHandler);
this.container1 = new Container { Object = this.eventHandler };
this.container2 = new Container { Object = new EventHandler(MyEventHandler) };
GC.Collect();
Console.WriteLine("container1: {0}", this.container1.Object == null);
Console.WriteLine("container2: {0}", this.container2.Object == null);
}
}
This produces this output:
container1: False
container2: True
which indicates that during the garbage collection that the second container had its EventHandler
garbage-collected "out from underneath it". This is by design the way that weak references work and the explanation for you need to keep a reference to it yourself.
精彩评论