Sunday, September 27, 2009

How to change event hanlders at run time

One of the challenges when using event handlers at run time , in my opinion , is that we can't manage previously registered event handlers very easily . I mean we don't know who else is listening to the same event.

Suppose we have two combo boxes and a list box.We want to let the user select a category in first combo box and select an object in second combo box and see a list of related information of that object in the list box.

User selects Music category from category combo box.
User selects "Abby Road" from objects comb box.
A list of album information is shown to him.

Here's the source:

private void CategorySelected(object sender,EventArgs e)
{
if(
((Categroy)_categoryComboBox.SelectedItem).Type==
CategroyType.Music
)
{
_itemComboBox.DataSource=_application.GetMusicAlbums();
}
}

private void ItemSelected(object sender,EventArgs e)
{

if(_itemComboBox.SelectedItem is MusicAlbum)
{
_list.DataSource=
_application.
GetMusicAlbumAdditionalInfo
((MusicAlbum)_itemComboBox.SelectedItem);
}
}


Well it's working but it's not very nice. As you can see we should write a lot of "if" to handle different kind of categories and objects.
OK we could change the event handler instead of using if.
Suppose that we have registered some event handlers in a dictionary then we can write :

_itemComboBox.SelectedItemChanged+=
_eventhandlersRegistry[selectedCategory.Type];



But we have a little problem : what if there's already a category selected ? so we should first remove last SelectedItemChanged event handler then we have to write:

_itemComboBox.SelectedItemChanged-=
_lastSelectedItemChangedEventHandler
_itemComboBox.SelectedItemChanged+=
_eventhandlersRegistry[selectedCategory.Type];
_lastSelectedItemChangedEventHandler=
_eventhandlersRegistry[selectedCategory.Type];


To solve this kind of scenario , I recently use a solution that I'd like to share with you:
I use a nested EventHandler delegate therefore there's no need to change the event handler of actual event .Here's what I do:
I have an event handler delegate declared as :

private EventHandler _nestedSelectedItemChangedEventHandler



Then I have this general event handler for the actual event :

_itemComboBox.SeletedItemChanged+=
GeneralItemChangedEventHandler;



When the category is changed I place a new delegate in my nested event handler like this:

_nestedSelectedItemChangedEventHandler=
_registeredEventHandlers[_selectedCategory.Type];


and GeneralItemChangedEventHandler will call nested event handler as follows:

private void GeneralItemChangedEventHandler (object sender,EventArgs e)
{
if(_nestedSelectedItemChangedEventHandler!=null)
_nestedSelectedItemChangedEventHandler(sender,e);
}



This approach can be used if you are implementing a strategy pattern so that you want to change the behavior by changing the state of your object and there's always a single event handler involved to handle an event.

No comments: