Now don’t get me wrong: compared to all other control suites I’ve used, ASP.NET AJAX has been, in terms of the whiz-bang-features-slash-make-my-life-easier vs. deal-with-the-hell-of-trying-to-make-other-people’s-code-do-exactly-what-I-want metric, the best tool. It gives my users a whole new experience, and actually integrates fairly seamlessly with my existing ASP.NET 2.0 web applications.
Then, inevitably, they get used to this and simply want more. That’s when I find myself having to pull off some fairly ghetto maneuvers to get my AJAX Control Toolkit controls and extenders to stay equal to the task. I can still get 80% of the way there fairly easily; that last 20%, however, has started to get further and further away.
(A brief aside: I haven’t blogged in while, mostly due to the fact that the stuff I’ve been working lately has actually worked without any of my wonderful hacks.)
So my client wanted to see a new page with the CollapsiblePanel Extender doing its thing on a mouse over event instead of a click (as all the other ones currently worked). “That’ll take me about two hours,” I estimated that morning. And indeed, as I was making lunch plans, I had everything working, and only needed to change the click event of the table cell that expanded my panel to an onmouseover. I was feeling rather good about this, thinking that it would be another win for AJAX (which I pimped hard to even get used at my conservative client). My Friday burrito would be delicious today.
Well, it sucked.
All I thought I had to do was set AutoCollapse and AutoExpand on the extender to true (as advertised), and life and burritos would go on. But no! It didn’t work. I played around with setting the TargetControl and the ExpandControl to both be the panel. Nothing. After going though all possible combinations of setting controls, playing with the auto properties, and modifying my HTML, (and a weird combination of swear-praying) nothing worked. So I took most of my frustrations out on my burrito, and plotted how I’d spend the second half of my day working though the last eighth of my problem.
It turns out that the solution was only about a dozen characters to type. What I had to do was hack the source of the control, recompile the AJAX Control Toolkit, and dump the new DLL into my project. To do this, first download the open source version of the Toolkit. Then in the web project, each control’s source files are organized into a folder. Open “CollapsiblePanel.”
So once I figured this out, I did a simple find and replace of “onclick” to “onmouseover.” This was sufficient to expand the control. If “AutoCollapse” is set to true, the “onmouseout” event of the panel and the ExpandControl are hooked to collapse the panel.
My other “hack” was to change this to “onmouseleave.” I know that this is an IE-only event, but this is for an internal site at my client that only uses Microsoft, so nee-ner-nee-ner-neeeeeee-nerrrr. But I promise that in any other situation, I would have considered cross-browser compatibility. I promise!
The difference between onmouseout and onmouseleave is, in fact, annoying. Consider a panel that has a child table control. If you set onmouseout on your panel, it will fire when you enter the table, since the focus of the cursor has technically left the panel, although you are still on it! The onmouseleave event, in my opinion, is much more intuitive, since it only fires when you leave the outer bounds of the control, not when you enter any of its children. Make sure to keep this in mind…
Now I apologize for not having any code examples. Since this is client code, I can’t paste it here, but I’ll describe how it all comes together. I have a table (cellpadding and cellspacing are zero) with three rows, each having three cells. The middle cell of the middle row contains my CollapsiblePanel. The cell above it has the text, and is where the user mouses over to have the panel shoot out the bottom of the cell. I relatively aligned the bottom row with a “top” attribute of -3px to get this effect. Mousing out of the top cell and the panel both collapse the panel. Finally, the cells to the left and right, and rows above and below are all hooked as the ancillary CollapseControls I copied-and-pasted to ensure that the panel will collapse, even if the user gets a little nutso with the mouse.
Now of course, after compiling this modified AJAX Control Toolkit DLL, the behavior of all CollapsiblePanels will be altered, so it’s best to create a new control by copy-and-pasting the files, and changing the name. In this fashion, you can create as many flavors of a control as you like. Of course, it’d be sexier to add a bunch of flexibility to the existing control to hook clicks or mouse-overs, but in the life of a consultant, time is always of the essence.
So I deployed my new hacked control, and everything worked great! I opened the page, and spent several minutes watching my beautiful panel expand into life and collapse into nothingness without a click of the mouse. I then showed my boss.
“What was that?” he asked.
“Reload the page and do it again.” Watching more closely, I did as he asked, and then saw what he saw.
The panel “flickered” on the load. Incidentally, I had never used a collapsible panel that started out collapsed. When you do this, the panel, as rendered by the HTML, technically starts out visible, and AJAX hides it at the last second…but not quickly enough. It was then 3:30, and I was still only 90% of the way home.
I tried setting the panel to initially be hidden. This apparently pissed off AJAX, and nothing happened on my mouse over; hidden panels aren’t visible… So to get around the flicker, I had to set the parent of the panel (which was that middle cell in the table…keeping the background color of both the cells and the table to same allowed me get away with this) to not be visible.
If the panel’s parent is not visible, the panel won’t flicker, but since its visibility is still set to “visible,” AJAX won’t complain. Once we are past the initial page load, we are golden, except for the fact that the panel’s container is still hidden. I was able to get away with setting the cell’s visibility to “visible” on its row’s onmouseover event. That fixed everything. I was able to leave at 5:00 a happy camper; such victories that you really have to fight for never make the day seem wasted.