Tuesday, August 12, 2008

ASP.Net Ajax PopupControlExtender gotchas

I recently completed a user control that included a PopupControlExtender which was used in repeater on the user control's host page. I'm using the version of ajax that ships with visual studio 2008 (pre sp1). I came across several gotchas:

  • Technically, the first gotcha is that my user control cannot include a ScriptManager since there can only be one per page, and since the user control is repeated many could be created. Thus, the host page must own the ScriptManager effectively rendering my user control with my PopupControlExtender non-self containing. I think this is a clumsy limitation of the current state of ajax.
  • The second gotcha applies regardless of the fact that there are more than one instance of the PopupControlExtender on the page: I don't want the object that pops up to be triggered when the referenced TargetControlID control receives focus (I can hardly imagine a scenario when someone would want this), I want it to pop up on a button click. Thus, I must pop it up manually. But when I do so, I must know (or learn by trial and error) that the client framework captures page events and hides my popup (so when you click on other controls outside your popup it closes). My pop function must look like the following. Without "event.cancelBubble" my popup won't pop up.
function pop(popExtenderID)
{
event.cancelBubble = true;
var popExtender = $find(popExtenderID);
// this one is the next gotcha
popExtender.set_OffsetY(popExtender.get_OffsetY() + document.body.scrollTop);
popExtender.showPopup();
}
  • This next one is also irrespective of the fact that my control is in a repeater. The PopupControlExtender doesn't seem to account for scroll position. Thus the call to set_OffsetY in the snippet above. Even so, sometimes the position of the popup will be at the very top of the page. I haven't been able to figure out why it doesn't always work; it doesn't seem to be consistent.
  • This one is certainly repeater related. The function above takes as a parameter the ID of a PopupControlExtender. If there are more than one, we have to accept the right one (the function is part of my user control so it will be repeated causing only the last one written to be the function that is used for all of my popups). So in the code behind of my user control, I had to add the following in order to pass in the "current" extender. Note that I had to ensure that the BehaviourID of the extender was unique. The replace call is to make the ID conformable to HTML rules.
var ExtenderBehaviourID = '_' + Guid.NewGuid().ToString().Replace( '-', '_' );
this.PopupControlExtender1.BehaviorID = ExtenderBehaviourID;
this.btnPop.Attributes.Add( "onclick", "pop('" + ExtenderBehaviourID + "');" );

  • This one was caused by the fact that I wrapped the controls within my pop up in an UpdatePanel because they made server postbacks. The code in the snippet above should only run when the host page loads or posts back, but not when my user control posts back. What I had is a search edit box/button and a GridView within my popup. The search button causes a postback and so does paging on the grid. Within these postbacks, I don't want my extender to receive a new unique id because if it does the javascript back on the page will receive the wrong ID - why I can't quite remember - I have a function that closes the popup. In this case "this.IsPostback" doesn't work (nor does this.Parent.IsPostback which maps to the same result) because this would represent the host page's postback. My hack elegant solution to this problem was to examine the BehaviourID of the PopupControlExtender. By default, that BehaviourID would contain the ID of the PopupControlExtender itself. If it is a postback of my control, I would've already changed that BehaviourID to be something like a guid (from the snippet above). So here's the condition:
private bool IsPostBackThisControl()
{
return !this.PopupControlExtender1.BehaviorID.Contains(PopupControlExtender1.ID);
}

Tuesday, July 08, 2008

Business Vs. Technical Validated

My previous post which posited that the old yarn that knowing the business domain is a more important skill for the programmer to improve than programming-specific skills is bunk has been partially validated by Joel Spolsky in this podcast.
In it, Jeff Atwood holds the opposing position, but Spolsky's comments were spot on. He did some significant work on a medical application w/o caring a whit about the medical field. I maintained an application which illustrated annuity performance w/o much caring about the specifics of annuities.
I just cannot understand Jeff's position. The specifics of how a given business works is brain filling fluff whereas programming related knowledge is a programmer's bread and butter.
I'm currently working on software which supports educational institutions, but learning more about educational institutions is never germane to making our software better.

Friday, June 06, 2008

Silverlight Bar Chart and Wizard Samples

I spent some time on some silverlight samples, so I thought I'd make them available to the world. Someone may find them useful as a starting point or just a learning exercise. These are done with VS2008 and silverlight 2.0 beta 1. They are:
  • A silverlight bar chart / graph application.
  • A silverlight wizard.
Bar Chart: The bar itself was stolen then modified from here. Note if you download it that clicking on the service button won't work, just the update from HTML.



Wizard: This was created by this guy. When I downloaded it, it didn't work. Note that the arrows are actually images (which probably shouldn't be used as long as you have XAML). It is a good model, though. The whole wizard is one control, all the data is in one thing, no postbacks and the next/back is just a smooth animation. It makes a wizard a very easy, clean thing.

Wednesday, February 27, 2008

Web Programming is a Hack, Part Deux

Our current app displays again why web programming is a just a hack. It's sort of a wizard approach - it has an intro, many steps, and a conclusion. To get to the next step of the wizard, the user clicks our "next" button. We also have a "back" button, but then a browser also has one of those thingies. Any developer or casual browser user knows where I'm going with this. "Back" and "Next" are for hyperlinked documents, not applications.
It's just so fundamental. We're sitting here debugging a problem that happens when the user mixes the browser's "back" with our "next". We're tempted to yell at users, "don't click that button", but it makes perfect sense for them to do so. The problem is that web apps are hacks. Browsers don't make good platforms.
Of course, we fixed the "bug" (by "fixed the bug" I mean "made a better hack"), but the overarching hack remains.



It sure seems to me that there needs to be something new for applications. Browsers work great for HTML, but why isn't there something else (just as ubiquitous and standardized) for applications? It seems like RESTful apps are much less hacky than web applications, but not quite there. Plug-ins like flash and silverlight embed applications in the browser, and while I know nothing about actionscript, silverlight can make for a full fledged clean application. AJAX, hell no, but application plug-ins maybe. But these plug-ins are always mixed with HTML...
It may be that the hack will continue for a very long time simply due to the fact that people keep making ever more hacks at an increasing rate.

Hack on, I suppose.

Thursday, January 31, 2008

Web Programming is a Hack

I'm surely not the first person to make this observation, but I don't understand why it's rare.

I've spent most of my career so far doing either non-web apps or things like web services that are cohesive programs. Now I'm doing web development full time, so it's in my face.

HTML/HTTP was (as we all know) not intended to be a systems development platform. Nowadays, darn near every system must run over it. That's a fallacy, of course; many web apps don't need to be so (how many corporate internal web apps are there that could be much simpler as desktop apps), but most people think it's true.

Everything is so fragmented in web development - you may have:
  • Code on the client for validation or whatever.
  • Different code on the client that has nothing to do with the other code (AJAX).
  • Code constructs in your CSS.
  • Code on the server.
  • Code directly on the page (sometimes necessary).
That's just the code; the process itself is enormously complicated - and I don't know the half of it. As I've said before, complex ain't cool. It's certainly possible to make some things clean in web development, but the whole process isn't.

Consider what I had to do recently in order to make a simple animated gif run while a lengthy process takes place. I fully realize there are other ways to do it (queuing the process and just returning a simple message seems the best candidate), and I'm constrained in this case by slightly older technology (.net 1.1), but still:
  • Client submits a batch of email messages. (Note: IE locks animated gifs while waiting for the return of a post).
  • Server receives request, needs to return quickly so it starts an asynchronous process to send the messages, sends down a javascript handler to the page to receive the results of the asynchronous process, and turns on the gif.
  • The page receives the result so it starts animating the gif.
  • The asynch process starts sending email messages maintaining a collection of those that may have failed.
  • When it finishes, it can return text to the client, but that handler can't receive complex results (JSON not avail, can't use a web service here), so all it receives is the number of successful email messages. It stuffs the failed collection in cache on the server.
  • Now the client receives the result, knows how many messages were successfully sent, but it doesn't have the data on the failure collection. It builds a url to post back to the server with a query string sending the number of successful email messages back.
  • The server detects the successful email query string, checks for a failure collection in cache, binds that to a grid (and turns it on) if it exists, writes out the successful number (couldn't have done that on the client because the state would be lost on the second trip to the server) and returns. Whew!
That's a hack. But I have to do what I have to do. The web constrained me. It's still fun, but it always feels kludgey.