Wednesday, June 02, 2010

Extending the .Net Menu Control

This is a cool trick that you can use to extend existing script libraries that do not have the built in extensibility features.

In one of the ASP.Net 2.0 projects I was using the ASP.Net menu control. ASP.Net menu control is a cool control with hell a lot of features and flexibility but I couldn't fulfill the need by using any of those.

When you go(mouse over) to a menu item in the menu control, it highlights the menu item. But when you navigate to the next level(sub menu) of the menu, highlighted menu item in the previous level will lose the highlighted characteristic. So if you have a quite big or a menu which has couple of levels and if you are in the menu item which belongs to a latter level of the menu control, you might not know the path of the menu. Bottom line is path of the menu item should keep highlighted(hovered) so the user can see the path by the highlighted menu items. The task was challenging because I could not find a built in feature or an extensibility hook in the menu control itself.

I was looking for the options to extend the control but menu control does not have client side object model or any support from DHTML side of the things which allows the extensibility. When I dig more into the problem I got to know that DHTML object model is just a big freaking hash table and interception will be a way to achieve flexibility/extensibility.

So the basic idea behind the solution is in the javascript interpreter, everything from objects to functions is an entry in the globally defined dictionary. Because of this structure and the fact that javascript is loosely typed, you can substitute anything with anything else. So the inject point of the extensibility of the menu control will be the DHTML portion of the menu control. i will store some of the built in javascript functions in to variables and then set my own functions to those built-in functions. Then I will call the built in function from my own functions then do the rest I need. Actually I'm overriding the built-in function. What a cool way to entend the control.

Then comes the next problem. How do I know the functions I need to override? This is bit cumbersome in ASP.Net 2.0 because of the way it stores and sends stylesheet and javascript files to the browser. I had to open the page source and get the reference to a webResources.axd file. By copying and pasting this reference into the browser, I was able download the stylesheet or javascript file that's being referred.

After downloading the relevant javascript file associated with the menu control, I see the 2 functions I want to intercept.
Menu_HoverDynamic - This is the function that calls when you move the mouse over a menu item
Menu_Unhover - This is the function that calls when you move the mouse off a menu item

Algorithm is pretty simple to retain the "hovered" characteristic in the parent menu item. I used a built-in javascript function in menu control. In mouse over event it finds the item's parent menu item and fires the "mouseover" event of that parent menu item as well. Conversely, when the user moves the mouse off the menu items, it finds the parent menu item of it and fires the "onmouseout" event on that parent menu item. There is no need of any recrusive processing thanks to event bubbling mechanism provided by the DHTML DOM.

The final code follows.

1: <'script' type="text/javascript" language="javascript">
3: var fw_Menu_Unhover;
4: var fw_Menu_HoverDynamic;
6: function SetupInterceptors(){ // called by onload event
7: fw_Menu_HoverDynamic = Menu_HoverDynamic;
8: Menu_HoverDynamic = my_Menu_HoverDynamic;
9: fw_Menu_Unhover = Menu_Unhover;
10: Menu_Unhover = my_Menu_Unhover;
11: }
13: function my_Menu_HoverDynamic(item) {
14: fw_Menu_HoverDynamic(item);
15: var x = Menu_FindParentItem(item);
16: if(x && x.tagName.toLowerCase() != "body")
17: x.fireEvent("onmouseover");
18: }
20: function my_Menu_Unhover(item) {
21: fw_Menu_Unhover(item);
22: var x = Menu_FindParentItem(item);
23: if(x && x.tagName.toLowerCase() != "body")
24: x.fireEvent("onmouseout");
25: }
27: <'/script'>

Here it goes. My custom functions replace the menu control's built-in functions and provide the desired result.

No comments:

Post a Comment