With the advent of ASP.NET, Microsoft developers were given superior tools for web application development in the beginning of the century. Many things has changed since the first betas of Visual Studio, some of us still remember the first book
available on the topic:
Some lessons learned were harder than others after all these years. Here are some of them of which I've twisted my brain many a times.
- Internet Explorer doesn't rule the planet anymore. Browsers are now
assembled in a crowded court and even death-sentences are not carried
out according to developers wishes
- ASP.NET wasn't that easy after all. Ref1 + Ref2
- Ajax is more than washing powder. No, I am not thinking of the dutch football team
The first two points were popped off the top of my stack
, but the latter is this blog's raison d'être
and in particular I will spend the gross portion discussing rendering strategies for ASP.NET.
Full re-rendering has been with us ever since aspx pages saw daylight. Regardless of POSTs and GETs, the ASPX was always fully sent (from top to bottom) to the client. Through it's elegant postback model, ASP.NET gave us server side event handlers and other treats like full OOP
and the BCL
just to name a few of it's premier citizens. The issue with full re-rendering today is that the approach does not concern itself at all with the concept of ajax
. Users are now adapted to browsing the web more as an interactive applications than a collection of static pages. People interact via chats, games, auctions and other applications. Full re-rendering breaks this flow and is certainly not the recipe for survival in this highly competitive industry. Microsoft's response to this challenge was a technology called partial rendering
PARTIAL RENDERING == PARTIAL SUCCESS
Microsoft has made several efforts to bring ASP.NET back in the leauge of extraordinary gentlemen, with (unfortunately) only Partial success. A few of the symptoms can be witnessed in the following observations
It's brought to my attention that the UpdatePanel strategy to Ajax on the ASP.NET platform is by far the most popular approach. Simply wrap up your existing ASP.NET code into these panels, drop the ScriptManager on the page and configure a few triggers and your existing ASP.NET app is ajaxified with just a few ingredients. What a wonderful stew!
There are however a few side-effects of using the UpdatePanel that is of great importance to be aware of. I will lay them out for you here:
Hence, the need for a new paradigm.
- The UpdatePanel is still a re-rendering strategy, the only difference is that it restricts the area of re-rendering and gives you partial rendering.
- To get a tight and quick response from the server - you still need a lot of UpdatePanels. In theory, for optimal performance, one needs an UpdatePanel for every server control. You also need to configure triggers between them and introduce the ScriptManager on the page. This will eventually lead to un-maintainable code and the famous "damned if you do and damned if you don't." scenario.
- The Client side HTML nodes is destroyed and replaced by the new nodes. This in itself doesn't bring any level of interactivity, but actually leads to limitations of it. Imagine an AutoCompleter/Google Suggest feature built with the UpdatePanel? My thoughts exactly!
Say hello to DRIMR (Dynamic Removals, Inserts, Moves and Replacements)
If the ASP.NET platform is to continue to be superior in it's domain (Active Server
Pages) there's a need for a shift, or rather going back to it's roots with simplifcation in mind
. Let's examine the proposal for a new concept called DRIMR.
Key goals for the DRIMR technology
Before we dig into the practicals, let's draw some theory on the blackboard first.
- Perform partial rendering as seldom as possible, but when required, render as little as possible.
- Keep the server side ControlTree synchronized with the Clientside DOM tree without replacing the DOM structure.
- Introduce as few new concepts to learn as possible. In the best case, no new concepts should be learned. All existing ASP.NET knowledge should be reused.
Let's imagine that ASP.NET itself took the responsibility of understanding all the tasks that were assigned to the UpdatePanel/ScriptManager and lifted out that responsibility from your shoulders. It would be just like when ViewState is marked dirty when changed after initialization. It happens automatically. Then imagine that each control only sent it's changes back to the client in the form of code
A lot of things can happen to a control over it's lifetime. Here are a few cases
- The control can be added
- The control can be removed
- The control can be moved between different parent controls or within it's parent childcontrols and change it's index
- The control can be added, but replaces an existing control in the client. (ie: Label replaces Label, or Eye for an Eye)
- The control stays where it is, but only a few properties change
All of the above scenarios incur a change that would require re-rendering. In partial rendering, the contents of the UpdatePanel is re-rendered. In the case of a moved control, often the source and target UpdatePanel is re-rendered leading to a substantial amount of data being sent over the wire. The data can often be comparable to a full page rendering.
Once again, imagine that all these cases were abstracted away into the controls themselves so that they would take care of those scenarios and that instead of re-rendering, instructions were sent to the client about what should be done. That's exactly what DRIMR is all about.
Let's cover a few of these cases and look at it's implications.
Control added during ajax request
The control is added in an asynchronous XHR
and it has never been rendered before. It should then automatically try to inject itself in the controls on the client. This requires extensive insertion capabilities on the client and a substantial intelligence from server. From a birds view, it would look something like this (Paint in Windows7 rocks!!!
This is typical code you would write to insert a new control at a given index from the server. The control could represent anything, that's not the most important thing for now. Later we'll look at some concrete implementations. DRIMR
would serialize something like this to the client. A single line of code to insert the node directly adjacent to the flagX control. This is refered to as the dynamic insertion capabilities.
Gaia.IA('flagX','<img id="flagA" src="tn.png" temp_src="tn.png" alt="Tn" style="border-width:0px;" />');
- Gaia.IA is an abbreviation
for Gaia.insertAfter and will insert the control adjacent to the 'flagX' control. This control doesn't have to be an AjaxControl. The
second parameter is the control html itself.
- Other insertion functions include IT - insertTop and IE -insertEnd
- Insertion of new nodes involve partial rendering to some degree, but is limited only to the content of the new control inserted. It's related script object is also initialized as part of script.
Control removed during ajax request
Removal of a control in an XHR callback only requires the server to send instructions of removal to the client. The only information needed is which ControlID to remove and the client will remove and destroy the control instance(s).
This yields huge benefits because no rendering takes place at all and all other controls are left untouched. This is the dynamic removal part of DRIMR
. The serialized code looks something like this:
- Gaia.DR is abbreviation for Gaia.dynamicRemove
Control moved during ajax request
Creating interactive applications and games often include moving controls. It's a shame that moving a control server side requires re-drawing the contents of the page when in practice the control is already there - in the browser.
A move could have been thought of as a combination of insertion and removal, but this would require re-rendering the control, when all you want is to have that very same control situated a different place on the page. Let's illustrate this with the chess game. Moving the knight from f3 to g1. The pseudo-code in the Dropped event is for educational purpose only and is codewise non-sensical.
In the case above, DRIMR should physically move the control in the browser. This is the dynamic move capabilities of DRIMR. This is accomplished with the following instructions:
Notice also the ID change. This is required since the generated ID from the server will change if it's part of an INamingContainer. If the ID doesn't change, the call to SetID will not occur. The naming container in the chess game is moved for readability. Click here to view the chess game online.
- Gaia.IT is abbreviation for insertTop
- All the IA, IT, IE functions are used in dynamic moves, but the second parameter is the source control ID and not the markup to insert.
- The call to .setID updates the ClientID to fully reflect the cumulative ID based on the INamingContainer on the server.
Control replaces another during ajax request
This case reveals itself quite often. The most obvious example is pagination in the GridView. When you go from Page1 to Page2, the controls are often the same( not always, but often). So if the same control is located during a swap based on it's coordinates, we should effectively be able to reuse that control just by diffing the different states and updating it.
Case: Label replaces a Label. Reuse that Label on the Client. Here's a practical code example: ASPXC#
protected void zFilter_TextChanged(object sender, EventArgs e)
zRepeater.DataSource = _calendarController.CalendarItems;
This is a a typical databinding scenario where Labels are used to represent each DataItem. Let's say the list got filtered during the TextChanged event of a Textbox and went from 4 items to 2 items. This is the code that would be transmitted to the client:
The first two labels were reused and since only the Text property changed - only the Text property is serialized back to the client. Interesstingly the Labels not needed anymore are dynamically removed using the Gaia.DR statement.
Only Control properties are changed
Often, controls are not added, replaced or removed. Sometimes only simple property changes happen. In this graphic you see 40 nested Panels inside eachother. They are made clickable and it's background-color is set to a randomly defined color in the click handler.
In C#, that's a simple one liner
protected void panel_Clicked(object sender, EventArgs e)
panel.BackColor = WebUtility.GetRandomColor();
Without a proper way to signalize that change, only partial rendering would come to the rescue and re-render all the panels again. But with the Update capabilities in DRIMR, you would only serialize the following change:
Case Study of DRIMR technology: GridView
This Case Study demonstrates how DRIMR applies to the ControlSmith. We will examine it's usage in the GridView for ASP.NET
When you databind a GridView - the controls are typically the same at all the same positions. Based on your ItemTemplates, the structure of the rows, columns and cells are mostly the same and therefore the controls can be reused in update scenarios. Let's say you click the Pager to navigate from Page1 to Page2; In the first column you store the CustomerName and a Label is used for this purpose. DRIMR kicks in and uses all the techniques described above to insert, remove, reuse and change the controls in the GridView.
Here's a little exerpt from the instructions serialized to the client
$G("m_p_zGridView_ctl07_ctl05").setText("9/13/2009 5:23:56 PM");
As you can see the labels are reused and their new state is assigned and serialized automatically.
Online Examples of DRIMR
DRIMR embraces the ASP.NET Server Control paradigm