Subscribe and unsubscribe to Windows.Forms.Controls events automatically

D

Douglas Peterson

I created the following code:

private struct StackItem
{
public EventHandler theEvent, theHandler;
public StackItem(EventHandler theEvent, EventHandler theHandler)
{
this.theEvent = theEvent;
this.theHandler = theHandler;
}
}

protected void AddHandler(EventHandler theEvent, EventHandler theHandler)
{
theEvent += theHandler; // add handler to event
stack.Push(new StackItem(theEvent, theHandler)); // note the fact that we
did so
}

public virtual void Dispose()
{
while (stack.Count > 0)
{
StackItem si = (StackItem)stack.Pop();
si.theEvent -= si.theHandler; // remove the handler from the event
}
}

So that my derived classes can register for various events of various
controls on my form without having to make certain to add both the subscribe
and unsubscribe lines of code. However, the following code:

AddHandler(form.listView.SizeChanged, new
System.EventHandler(this.listView_SizeChanged));

gives the following error:

"The event 'System.Windows.Forms.Control.SizeChanged' can only appear on the
left hand side of += or -="

I assume this is some attribute of the SizeChanged member of the various
stock controls. Is there any way around this?

Why do I need to unsubscribe? Because I'm using objects to implement various
states in my application. When the app changes states it needs to unhook the
current state object from all events so that the new state can take over.
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi

Did you used to program in VB.NET ?

This is usually the approach taken in Vb.net to add a handler to an event,
in C# you use:
form.listView.SizeChanged += new
System.EventHandler(this.listView_SizeChanged) ;
 
C

Christof Nordiek

Douglas Peterson said:
I created the following code:

private struct StackItem
{
public EventHandler theEvent, theHandler;
public StackItem(EventHandler theEvent, EventHandler theHandler)
{
this.theEvent = theEvent;
this.theHandler = theHandler;
}
}

protected void AddHandler(EventHandler theEvent, EventHandler theHandler)
{
theEvent += theHandler; // add handler to event
stack.Push(new StackItem(theEvent, theHandler)); // note the fact that we
did so
}

public virtual void Dispose()
{
while (stack.Count > 0)
{
StackItem si = (StackItem)stack.Pop();
si.theEvent -= si.theHandler; // remove the handler from the event
}
}

So that my derived classes can register for various events of various
controls on my form without having to make certain to add both the
subscribe and unsubscribe lines of code. However, the following code:

AddHandler(form.listView.SizeChanged, new
System.EventHandler(this.listView_SizeChanged));

gives the following error:

"The event 'System.Windows.Forms.Control.SizeChanged' can only appear on
the left hand side of += or -="

I assume this is some attribute of the SizeChanged member of the various
stock controls. Is there any way around this?

The cause is, that SizeChanged is an event and not a delegate field or
property.
Events can only be used on the left side of += or -= operators.
(An exception are field like events wich inside!! the class where they are
defined behave like fields of a delegate type.) Even if SizeChanged was a
field or property of a delegate type your code wouldn't work, because
delegates are imutable, and the operations would create only new delegate
instances wich will be stored in your stack item, but not in the Control.
Why do I need to unsubscribe? Because I'm using objects to implement
various states in my application. When the app changes states it needs to
unhook the current state object from all events so that the new state can
take over.


possible solutions:
1. for every Event make a static method, wich is subscribed once and calls
the appropriate handler on the actual stateobject.

2. make two methods wich do all the subscribing and unsubscribing of the
event.

3. store the old state object, and for every event make a method or
codeblock wich does the subscribing and the unsubscribing:
if (olsState != null)
control.Event -= oldState.Handler();
control.Event += newState.Handler();

4. in the StackItem store the MemberInfo of the event.
 
D

Douglas Peterson

Christof Nordiek said:
possible solutions:
1. for every Event make a static method, wich is subscribed once and calls
the appropriate handler on the actual stateobject.

This was my original method. The form handled the events and called virtual
members on the state objects. I didn't like it because it wasn't very
encapsulated.
2. make two methods wich do all the subscribing and unsubscribing of the
event.

This is the way I'm doing it currently, the objects subscribe in their
constructors and unsubscribe in the virtual Dispose method. This is tedius
and prone to mistakes. If I don't find another solution, I'll stick to this.
3. store the old state object, and for every event make a method or
codeblock wich does the subscribing and the unsubscribing:
if (olsState != null)
control.Event -= oldState.Handler();
control.Event += newState.Handler();

The different state objects don't all subscribe to the same events, thus the
desire for encapsulation.
4. in the StackItem store the MemberInfo of the event.

This looks promising. I don't know C# well enough at this point to make it
work so I am going to research it and learn something new.


There is a #5 as well that has since occurred to me:

5. Have the state objects create and destroy the controls.

Each state makes use of 95% of all the controls. The controls simply behave
a bit differently depending on the current state. There are quite a lot of
controls. So I don't like this one either.

Thanks for all the suggestions,
Douglas
 
D

Douglas Peterson

After researching MethodInfo, turns out I need EventInfo. It works great!
Here is the final code:

private class StackItem
{
public object control;
public System.Reflection.EventInfo eventInfo;
public System.EventHandler handler;
public StackItem(object control, string eventName, System.EventHandler
handler)
{
this.control = control;
this.eventInfo = control.GetType().GetEvent(eventName);
this.handler = handler;
this.eventInfo.AddEventHandler(this.control, this.handler);
}
public void Dispose()
{
this.eventInfo.RemoveEventHandler(this.control, this.handler);
}
}

private System.Collections.Stack stack = null;

public void Dispose()
{
while (stack.Count > 0)
(this.stack.Pop() as StackItem).Dispose();
}

protected void AddHandler(object control, string eventName,
System.EventHandler handler)
{
if (this.stack == null)
this.stack = new Stack();
this.stack.Push(new StackItem(control, eventName, handler));
}


I simpy call AddHandler(form.listView, "SizeChanged", new
System.EventHandler(this.listView_SizeChanged)) in my derived State object's
constructor.

Thank you for your assistance,
Douglas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top