UI and Ajax Recipes
The following recipes represent common development tasks when building Ajax-style applications using Volta.
Building User Interfaces
Objective
We want to write a declarative UI in a cross-browser compatible way, in a manner
that resembles building WinForms applications.
Rationale
The structure of a WinForms application is very different from that of a browser-based
application. Those differences are accidental rather than essential, stemming from
the histories of the underlying technologies.
The Recipe
Ultimately, Volta will reduce the differences to zero, such that the exact same
application code works on the desktop or in the browser. Until then, we reduce the
pain by supporting the browser-supplied HTML DOM APIs directly, without change.
Consider the following HTML fragment. This is layout for a matched pair of text
boxes and buttons. Most of the tags specify the positions of these elements in a
table.
<h3>Find</h3>
<div>
<table>
<tr>
<td>
What</td>
<td>
<input
id="what"
type="text"
style="height: 20px;"
/></td>
</tr>
<tr>
<td>
Where</td>
<td>
<input
id="where"
type="text"
style="height: 20px;"
/></td>
</tr>
<tr>
<td
colspan="2"
align="right">
<button
id="find"
style="height: 20px;">Find
it!</button>
<button
id="clear"
style="height: 20px;">Clear</button>
</td>
</tr>
</table>
</div>
In Visual Studio we could build the UI in a graphic designer or in an HTML editor.
The highlighted elements in the figure below correspond to the above HTML fragment—they
won’t be highlighted in the final output. However we could use any other HTML editor
to build the user interface. We like using the graphical designer because it makes
the experience similar to the WinForms experience.

Under Volta, the following code queries the contents of the two textboxes. We recommend
manually placing it in the InitializeComponent partial method. The DOM elements
must have unique IDs, which we use to query or modify them.
string what = Document.GetById<Input>("what").Value;
string where = Document.GetById<Input>("where").Value;
// processing of user supplied inputs follows
Reacting to User Actions
Objective
GUI applications must respond to user actions, such as clicking a button or selecting
an element from a list. WinForms supplies an established programming model, namely
.NET events for this task. >
Rationale
We avoid deviating from established models in this area because we don’t wish to
introduce more accidental complexity. Consequently we want to amplify the ways in
which the WinForms and web programming models are similar. This keeps the marginal
incremental concept count for Volta as low as possible.
The Recipe
Fortunately the DOM event model and the WinForms event model are almost identical.
Therefore the same coding style works for both.
Consider the following code fragment. It shows wiring the “Find” button to a method
that handles the Click event.
findButton.Click += findButton_Click;
//
void findButton_Click()
{
string what = Document.GetById<Input>("what").Value;
string where = Document.GetById<Input>("where").Value;
// processing of user supplied inputs follows
}
Page-level Display Morphing
Objective
In the past most Web applications comprised multiple pages. The trend is away from
that. Ajax-style applications avoid full postback to improve the user experience.
Rationale
Legacy Web 1.0 applications break the presentation into multiple, distinct pages.
This is a problem even for new applications because developers have been conditioned
to design this way.
Recipe
Currently, a Volta application must have a single HTML page. We easily simulate
the effect of multiple HTML pages as typical in Ajax applications. This is good
news for everyone because the world is moving in that direction.
Volta applications leverage Ajax and
CSS to update DOM elements, including the complete contents of the page
(e.g., the Page Morphing Ajax pattern).
We simulate page flow by selectively showing and hiding elements of the active page.
This idea extends far enough to support DHTML-based 3D animation, as shown in the
samples installed with Volta.
In the following example none of the elements participating in the page flow are
visible in the HTML document. We keep them out of sight through the CSS style
attribute (alternatively, we could define styles for visible and invisible elements
and use the class attribute).
<div id="defaultSampleCode"
style="display: none">
<pre>
void ShowDefaultMap()
{
map = new Microsoft.LiveLabs.Volta.VirtualEarth.Map(mapDiv);
map.LoadMap();
}
</pre>
</div>
We make elements visible by changing their display style. We could implement these
changes with .NET 3.5 extension methods:
static class
HtmlElementExtensions
{
public static
void Show(this
HtmlElement element)
{
element.Style.Display = "block";
}
public static
void Hide(this
HtmlElement element)
{
element.Style.Display = "none";
}
}
Finally, we can then simulate navigating from one page to anther (i.e., page flow)
by hiding all the elements from the inactive pages, and showing the ones from the
active page:
foreach (var
element in visibleElements)
// hide all "inactive" pages
element.Hide();
code.Show(); // "active" page
Invoking Services
Objective
Nowadays, it is difficult to write an application that doesn’t employ services.
Something as simple as obtaining a weather report is infeasible or even impossible
without accessing web services.
Without Volta, there are too many different ways of doing the same thing, depending
on the language, the platform, and the tier. For instance, currently, the most common
way to invoke services from JavaScript is through XmlHttpRequest, and the most common
way to invoke services from .NET is through System.Web.HttpRequest: two different
ways of doing the same thing.
Rationale
Why should code that accesses services depend on where the code is running? Why
should code that looks up the weather from the browser differ from code that looks
up the weather from the server? Any differences are accidental.
Recipe
By stretching the reach of the .NET platform to cover the browser, Volta eliminates
the accidental complexity. Volta lets us access the service in the same way everywhere.
As a consequence, it allows us to tier split our application without breaking the
parts that access the services.
The Volta libraries provide identical XmlHttpRequest object abstractions on both
client and server. The Volta libraries preserve the semantics of this object, as
typically used in non-Volta Ajax-style applications and documented on
MSDN or as the Ajax XmlHttpRequest
Call pattern. Therefore, developers already familiar with it don’t need
to learn anything new.
Consider the following code fragment where we invoke an external service. The uri
variable holds the service location, and the call is synchronous, as signaled by
the third argument of the Open method.
var xhr = new
Volta.Xml.XMLHttpRequest();
xhr.Open("GET",
uri, false);
xhr.Send();
if (xhr.Status == 200)
return xhr.ResponseText;
else
return null;
Once the Send method returns, the Status property contains the HTTP status code
as documented. It’s just as easy to call the service asynchronously by changing
the third argument of Open to true:
var xhr = new
Volta.Xml.XMLHttpRequest();
xhr.Open("GET",
uri, true);
xhr.ReadyStateChange += delegate
{
if (xhr.ReadyState == 4)
{
if (xhr.Status == 200)
// Carry on
else
// Error
}
};
xhr.Send();
If we are interested in the server’s response, simply add a delegate to the ReadyStateChange
property as shown above. Once the ready state changes, the Status and ReadyState
properties contain the appropriate values, as documented.
The point here is that Volta gives us one abstraction that works regardless of where
we use it.