CoverStory
LANGUAGES:XAML | JavaScript | C#
ASP.NETVERSIONS: 3.5
A Nice Pairing
Silverlight and ASP.NET AJAX
By Shawn Wildermuth
Creating Silverlight 1.0 applications can be much likecreating typical HTML-based pages. Very often it is about displayinginformation to the user to entertain, inform, or educate. HTML and Silverlightboth do this quite well. But in today?s world, the Internet browser is fastbecoming an application platform itself. This means you can create trulyinteractive applications directly in the browser. Silverlight is a naturalsolution for creating these experiences on the Web and in the browser. In thissame sense, these interactive applications would like to be able to communicatewith data centers and servers. Because Silverlight 1.0 applications exist ontraditional Web pages, we can use well-worn techniques for communicating withthe server. This is where ASP.NET AJAX presents a great opportunity torepurpose this technology for use with Silverlight. In this article, I?ll diveinto the why and how of using ASP.NET AJAX with your Silverlight applicationsor controls.
The Problem
Creating standalone applications with Silverlight can bevery compelling. The first time you create a media player or game withSilverlight is an exciting time. But in the majority of cases, the real powerof Silverlight applications will be in creating connected applications.
At first blush, many developers new to Silverlight find itdifficult to understand that they are writing applications meant to be runningwithin the client?s browser. The Silverlight story is all about runningclient-side code. The drawing palette and animation stack are great, but it isforcing us as developers to look at writing code that runs in the client again.This is a jarring experience for many ASP.NET developers. Many may have becomecomplacent, trying to do all the coding on the server to avoid the complexitiesof the client-side development model.
Once the connected application development is being doneon the client, we must have a cohesive way to communicate with the server. Wecould write Web services for the communication layer, but calling Web servicesfrom client code is often difficult and complex. In addition, it would be niceif we had a way of writing our client code to hide some of the cross-browserbugs that tend to cause problems between the way different browsers haveinterpreted the specifications (or to what extent they?ve implemented thosestandards). That?s where ASP.NET AJAX can really help us integrate withSilverlight, as well as much of the code we are going to write on the client.
Pairing Silverlight with ASP.NET AJAX
Using your Silverlight application with ASP.NET AJAX is anatural pairing, as AJAX is allabout providing services to the client-side code. Let?s start with a simpleSilverlight control that shows a star rating that allows users to select arating (see Figure 1).
Figure 1: A simple Silverlight ratingscontrol.
To convince you of the natural pairing with ASP.NET AJAX,I?ll show you two types of integration. We?ll host our RatingsControl in an AJAXpage, then create a Web service that allows us to communicate bi-directionallywith the server.
Hosting Silverlight with ASP.NET AJAX
Like other Silverlight projects, we need to use the Silverlight.jsAPI to load our control on to our page. This is normally done with a JavaScriptfunction named createSilverlight, which is generated by the Silverlight 1.0templates. In our example, I?ve renamed this function as createRatingsControl,and added a parameter for the HTML host so I can host this multiple times on asingle page (see Figure 2). Normally, the template calls this creation functionwithin a script block on a particular HTML (see Figure 3).
function createRatingsControl(host)
{
var scene = newRatingsControl.Page();
Silverlight.createObjectEx({
source:"Page.xaml",
parentElement: host,
id:"SilverlightControl",
properties: {
width:"150",
height:"25",
version:"1.0",
isWindowless:"true",
background: "transparent"
},
events: {
onLoad:Silverlight.createDelegate(scene, scene.handleLoad),
onError: null
});
}
Figure 2: ThecreateRatingsControl function.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>RatingsControl</title>
<scripttype="text/javascript" src="Silverlight.js"></script>
<scripttype="text/javascript" src="Page.xaml.js"></script>
</head>
<body>
<divid="silverlightControlHost">
<scripttype="text/javascript">
var host =document.getElementById("silverlightControlHost");
createRatingsControl(host);
</script>
</div>
</body>
</html>
Figure 3: Typical creationcode in Silverlight.
In this approach, we are loading the scripts required inthe header and calling the creation function (createRatingsControl) in the bodyof the div that we want to host our Silverlight control. The problem with thisapproach is that for a simple HTML page, we can be virtually certain thescripts will load in time for the function to be available once the div isreached. As a page gets more and more complex, it becomes more difficult to becertain about the load order of scripts. We could move the script block to theend of the page to increase the chance that the scripts are loaded in time.
Luckily, ASP.NET AJAX comes to the rescue by providing acouple of services that help us solve this problem. Using ASP.NET AJAX requiresthat we use a server-side control called a ScriptManager. As we?ll see in thisarticle, the ScriptManager control provides a number of services to our page.To get started with solving our loading issue, let?s first drop a newScriptManager on our page (see Figure 4).
<%@ Page Language="C#"
AutoEventWireup="true"
CodeBehind="Default.aspx.cs"
Inherits="RatingTester._Default" %>
<!DOCTYPE html PUBLIC "..." "...">
<html xmlns="http://www.w3.org/1999/xhtml" >
<headrunat="server">
<title>RatingsControl Tester</title>
</head>
<body>
<formid="form1" runat="server">
<asp:ScriptManagerrunat="server"
ID="theScriptManager">
</asp:ScriptManager>
</form>
</body>
</html>
Figure 4: Our newScriptManager.
By including this control, we are effectively telling thepage to be prepared to use some AJAXfunctionality. Next, we need to add our scripts to our new AJAX-enabled page. Todo this, we?d normally add script tags to the page (usually in the header) toimport them into our page. In ASP.NET AJAX, we?ll instead allow the ScriptManagerto manage them for us. The benefit of using the ScriptManager is to ensure thatthe scripts are loaded in a timely fashion, as well as ensuring that everyscript is only included once. The duplication functionality in theScriptManager may not seem obvious, but as you use master pages and controls,you might run into multiple places where the same script might be required bydifferent parts of a particular page. But using the ScriptManager, itguarantees that all the scripts on a particular page are not repeated (whichcan cause a number of different issues).
To let the ScriptManager import our scripts, we?ll add anew Script tag inside the ScriptManager, then add each of our scripts insidethat new tag (see Figure 5).
<asp:ScriptManager runat="server"
ID="theScriptManager">
<Scripts>
<asp:ScriptReferencePath="~/silverlight.js" />
<asp:ScriptReferencePath="~/Page.xaml.js" />
</Scripts>
</asp:ScriptManager>
Figure 5: Adding scriptsto the ScriptManager.
Once our scripts are imported using the ScriptManager, wecan add a new div to host our Silverlight control:
<p>This is a tester for the RatingsControl.
Please select a rating!</p>
<div id="ratingHost">
</div>
We now are ready to load our Silverlight control into thediv named ratingHost. Instead of arbitrarily loading a script somewhere on thepage, hoping it is called only after the scripts are loaded on a page, we canuse another service of ASP.NET AJAX: the pageLoad function. ASP.NET AJAXsupports a specially named function, pageLoad, that is called after the scriptsare loaded. It?s like an OnLoaded event in ASP.NET. To use it, simply define iton a particular page (see Figure 6).
<script type="text/javascript">
function pageLoad()
{
var host =$get("ratingHost");
createRatingsControl(host);
}
</script>
Figure 6: Using thepageLoad function.
Note that this function is case-sensitive (so make sure itis capitalized as pageLoad). In Figure 6 you should see that we are calling thecreateRatingsControl, just as we did earlier in the div?s script block. Theonly change is that we are using another feature of the ASP.NET AJAX framework,the $get function. This function is a cross-browser-friendly version ofdocument.getElementById (or similar methods). Not only is it easier to type,but it will work on more browsers than even Silverlight.
We now have our control hosted on an ASP.NET AJAX page and,by using some of the services provided, we should have a more stable story thanwe would?ve had in traditional ASP.NET hosting of the Silverlight control. Next,we?ll delve into the communication with the server, which is at the heart ofthe AJAX stack.
Communicating with the Server via AJAX
In my interaction with many different types of developers,I run into numerous people who are actively using ASP.NET AJAX. A majority ofthese developers are using it for the controls: UpdatePanel and the AJAXControl Toolkit. While these controls are certainly laudable for theirusefulness, I think the real power of ASP.NET AJAX is the ability to create asimple server communication API.
For our example we want to be able to communicatebi-directionally with the server. Bi-directional communication with AJAX isreally a push-pull model. Because the HTTP protocol is connectionless, we needto have our client code initiate all the communications. When we need to tellthe server something, we need to send it a message; whereas, if we need to getinformation from the server, we need to make a server request. For developersused to non-Web-based communication, this is a different mindset that they?llneed to get used to.
ASP.NET AJAX allows us to create a simple client-sideJavaScript API by providing two services: Proxy Generation and Marshalling.Essentially, ASP.NET AJAX allows us to expose particular Web services to theclient-side JavaScript and create proxy class(es) for Web services to make themsimple to call. In addition, ASP.NET AJAX will convert types between the clientand the server (using JSON) so we can use complex types, as well as simpletypes from JavaScript without having to do much in the way of type conversion.These services will become clearer as we develop our Web service and use it incode.
In our RatingsControl, we want to communicate two things:set the ratings for a particular item and get any previously rated items. To dothis, we want to create a simple Web service that exposes two methods:SetRating and GetRating. Let?s start with the SetRating call. We can define ourtraditional ASP.NET Web service like that shown in Figure 7.
/// <summary>
/// Service to get and set ratings for use with the SilverlightRatings Control
/// </summary>
[WebService(Namespace ="http://adoguy.com/ratingservice")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class RatingsService : System.Web.Services.WebService
{
}
Figure 7: The Web service.
The only difference between a traditional ASP.NET .asmxservice and one that will work with ASP.NET AJAX is the addition of theScriptService attribute. This attribute tells ASP.NET that we want to be ableto use the service from ASP.NET AJAX; therefore, all types must be convertibleto JavaScript Object Notation (JSON). JSON is a format that is easy forJavaScript to deal with, is compact, and is a data-only copy of structures. JSONis not a proxy to the real object, but instead is equivalent to a copy of thestructure of the data within an object. See the Resources section at the end ofthis article for more information on JSON. ASP.NET AJAX uses JSON under thecovers to convert objects across the wire.
Next, we?ll want to add our SetRating method to allow usto tell the server that someone has selected a rating. In our simpleimplementation, we are allowing the client code to specify a name for therating, and we are simply storing it in an ASP.NET Session (instead of storingit in a database or file). This approach is not appropriate for most cases, butit makes our example very concise and easy to understand. Here?s our SetRatingmethod:
[WebMethod(EnableSession = true)]
public void SetRating(string ratingName, double rating)
{
Session[string.Concat("RATINGFOR", ratingName)] = rating;
}
The EnableSession property on the WebMethod is only requiredbecause we are using Session to store our rating. When this method is called,we simply store the rating in Session for later use. The real magic for ushappens when we go back to our page and tell the ScriptManager that we want touse this Web service on a page (see Figure 8).
<asp:ScriptManager runat="server"
ID="theScriptManager">
<Services>
<asp:ServiceReferencePath="~/RatingsService.asmx" />
</Services>
<Scripts>
<asp:ScriptReferencePath="~/Silverlight.js" />
<asp:ScriptReferencePath="~/Page.xaml.js" />
</Scripts>
</asp:ScriptManager>
Figure 8: Adding aWeb service to the ScriptManager.
By adding a Services section to the ScriptManager, we canadd any Web services for which we need a JavaScript proxy on the client side. Inthis example, we create a ServiceReference to our RatingsService itself. Oncethis is added, it allows us to use the new proxy from JavaScript. TheRatingsService itself is created as a JavaScript class in the client that wecan call directly. Here?s our use of this new Proxy class in code:
// Let the server know that a rating was picked
if (RatingsService) // Is the defined?
{
var svc = newRatingsService();
svc.SetRating("TestRating", this.currentRating);
// Fire and Forget!
}
This code first tests to see if the RatingsService isdefined as a class. By testing this we can write this code to work both in anASP.NET project that has it defined and a test page that doesn?t have access tothe Web service. Once we are satisfied that it does exist, we can simply createa new instance of the service using the new syntax. The service instance isreally just a proxy that knows how to call each of the methods asynchronously. Inour case, we want to call the SetRating method. Because we want to let theserver know the value, we can simply set the value to match our signature(where we want the name of the rating, then a number that represents the actualstar rating, which is stored in a local class variable in our Silverlightproject named currentRating).
More interestingly is if we request data from the server.To show this, we have a new method named GetRating that takes the name of therating from the client and returns the actual rating. The Web serviceimplementation is shown in Figure 9.
[WebMethod(EnableSession = true)]
public double GetRating(string ratingName)
{
object rating =Session[string.Concat("RATINGFOR",
ratingName)];
if (rating == null)return -1.0;
else return (double)rating;
}
Figure 9: The GetRatingimplementation.
In this code we attempt to find the rating in the Session;if we don?t find it, we return -1; otherwise, we return the actual rating. Inour client code, we want to call this new method and have access to thereturned number (see Figure 10).
// Get the Current Rating From Web Service
if (RatingsService) // Is the defined?
{
var svc = newRatingsService();
svc.GetRating("TestRating",
Silverlight.createDelegate(this,
this.getRatingComplete));
}
Figure 10: Callingthe GetRating Web service.
Just like before, we want to be sure that the Web serviceis defined, and, if so, create a new instance of the service. Unlike before,when we call the method, we are specifying a callback function (in this case,we are using the Silverlight.createDelegate call to allow us to call back intoa member of our implementation class). Calling GetRating will asynchronouslycall the server, then call our getRatingComplete function with the result ofthe Web service call once it completes. The getRatingComplete function is shownin Figure 11.
getRatingComplete: function(rating)
{
// Set the current ratingand recalculate it
if (rating >= 0)
{
this.ratingPicked =true;
this.setRating(rating);
}
},
Figure 11: The callbackfunction.
In the callback function, we are being passed the rating(which is just the return type of our Web service). If the rating is greater thanor equal to zero, we know the service found a valid rating and we can tell thecontrol that a rating was picked and set the rating (to show the correct numberof stars).
Conclusion
Silverlight represents a new platform in the Microsoft Web-space.Instead of reinventing the entire stack or forcing developers to use asingle-use set of technologies, Silverlight and ASP.NET AJAX represent anexample of using existing skill-sets in these new development models. Byleveraging existing knowledge, Silverlight can fit comfortably into existing Websites and developer toolboxes. Using ASP.NET AJAX?s Web service proxy creationservices allows the developer to continue to focus on the functionality andconcern themselves less with the plumbing. At the end of the day that means we?llbe able to deliver better solutions in less time.
Resources
Microsoft?s Silverlight Site: http://silverlight.net
The Silverlight Tour: http://silverlight-tour.com
JSON Defined: http://en.wikipedia.org/wiki/JSON
My Blog: http://adoguy.com
The source codeaccompanying this article is available for download.
Shawn Wildermuth isa Microsoft MVP (C#), MCSD.NET, MCT, and the founder of Wildermuth ConsultingServices, LLC, a company dedicated to delivering architecture, mentoring, andsoftware solutions in the Atlanta, GAarea. He also is a speaker on the INETA Speaker?s Bureau, and has appeared atseveral national conferences to speak on a variety of subjects. He currently isteaching Silverlight across the country during his Silverlight Tour (http://silverlight-tour.com). Shawnalso is the author of several books, including PragmaticADO.NET (Addison-Wesley), and co-author of four Microsoft CertificationTraining Kits for MS Press, as well as the upcoming book, Prescriptive Data Architectures. He has beenwriting articles for a number of years for a variety of magazines and Web sites,including MSDN, MSDN Online, DevSource, InformIT, Windows IT Pro, TheServerSide .NET, ONDotNet.com, and Intel?s Rich Client Series. Shawn hasenjoyed building data-driven software for more than 20 years. He can be reachedat his Web site at http://www.wildermuthconsulting.com.