n addition to the XAML and JavaScript combination supported in Silverlight 1.0, this Silverlight 1.1 Alpha client also supports managed code written in a language such as C# or VB.NET. That single addition brings many more development possibilities into play, and also pulls in a host of exisiting .NET developers, who can immediately take advantage of the new managed code feature.
As an illustration of the power available by combining .NET code, JavaScript, XAML, animations, web services, isolated storage, and of course, HTML, this article shows you how to build a “user viewer” application using Microsoft Silverlight 1.1 that displays user information via a rich and intuitive interface. The user viewer application shows a list of users in a card-like view, much like a Rolodex. Each card contains baseline information about a user. When a user can select a specific card to view, it flies out and enlarges, displaying a detail view.
Running the application—or any Silverlight application—requires only that the client machine has the appropriate Silverlight plug-in installed. When clients without the correct plug-in attempt to run the application they are prompted to download that plug-in version. Silverlight currently supports all major browsers on both Windows and Mac OSX.
Silverlight applications can be hosted on any web server, because they are just serving out HTML. However, if your Silverlight application is embedded within an ASP.NET application, or it uses ASP.NET AJAX Web services (as the sample application does), you’ll have to host it on IIS.
Expression Blend is a great tool for designing your user interface and animations, but you will need to use Visual Studio to edit the managed code to go along with it. Note that Blend does not currently include Intellisense support for XAML, but Visual Studio does, so you will also want to use Visual Studio when writing XAML manually.
What You Need |
To develop Silverlight 1.1 Alpha applications you need: |
Rolodex List
![]() | |
Figure 1. Changing Opacity: Hovering over a card brings it forward and increases its opacity. |
The sample application’s Rolodex-like view initially presents the cards with a very low opacity, so it can display all the cards in a relatively small area on-screen. Hovering over a card increases its opacity and brings it to the forefront of the list. Similarly, moving the mouse pointer away from a card returns that card’s opacity to its default low state (see Figure 1).
The cards are overlaid to make efficient use of screen space. The application achieves the overlay effect by incrementing the Z index and increasing the top and left values for each successive card—although you should note that child control locations are not absolute, they’re relative to their parent.
The user card itself is a custom Silverlight control. Whenever you begin creating a custom control, add a private field to keep a reference to your newly created control:
// Keep a reference to parent canvas for later // use in finding child objects. _control = (Canvas)this.InitializeFromXaml( new StreamReader(s).ReadToEnd());
There are a couple of things to keep in mind when creating a custom control. All custom controls must be compiled as embedded resources, which allows the Silverlight client to access their XAML via the manifest resource stream of the assembly:
Stream s = this.GetType().Assembly. GetManifestResourceStream( "UserViewer.UserCardControl.xaml");
Be very careful when you copy and paste an existing control to create a new one; it’s easy to forget to change the file reference, which means you’ll load the XAML for the wrong control. If you look into the manifest collection you will see all the XAML files for your application’s controls. You can inspect all the available resource streams by drilling into the following collection:
string[] manifestResources = this.GetType().Assembly.GetManifestResourceNames();
Loading Card Data
The application retrieves the data for the card list from a standard ASP.NET AJAX Web service. You need to add the ScriptService attribute to the Web service class and the ScriptMethod attribute to the methods of that class to allow Silverlight to call the service methods.
Currently, Silverlight does not allow cross-domain service calls, so any services you wish to consume must be hosted on the same domain as your Silverlight application. However, you can get around this restriction by simply employing an intermediary service hosted on the same domain as your Silverlight application that calls out to a remote Web service.
User Settings
It’s useful to let users control the maximum number of cards to display in the rolodex view. The application supports that feature via a simple class that exposes a publicly assessable property. Rather than making a web service call every time the application needs these settings, it’s much more efficient to read them from the client. This technique is where isolated storage comes to the rescue.
Silverlight 1.1 provides access to Microsoft’s isolated storage functionality. The isolated storage functionality provided by Silverlight is based on the isolated storage functionality in the .NET framework.
Author’s Note: Isolated storage access is available only in Silverlight 1.1 version through managed code; there is no JavaScript equivalent, so Silverlight 1.0 does not have this ability. |
Each Silverlight application has access to its own unique location in isolated storage. As its name implies, isolated storage is sectioned off from the rest of the file system in a low-trust area. Within an application’s isolated storage area, the application pretty much has full reign to manipulate files and directories. The only restriction is that the amount of data stored in isolated storage may not exceed 1 MB for a single application.
To access isolated storage, the application first obtains a reference to an IsolatedStorageFile object, which then provides access to a special IsolatedStorageFileStream type:
IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication(); IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(_settingsFilename, FileMode.Open, isoStore))
Using that file stream you can read the isolated storage data; in this case the application reads that data into a string:
JavaScriptSerializer jsonSerializer = new JavaScriptSerializer(); String buffer = reader.ReadToEnd(); _currentUserSettings = jsonSerializer.Deserialize (buffer);
The application serializes data in the settings file using JavaScript object notation (JSON), a format that’s much less verbose than XML. ASP.NET AJAX Web services use the JSON serializer to decrease the size of data sent over the wire.
Previewing Cards
Because all user cards are initially displayed stacked, partially transparent, and with only part of their information visible, users need a simple way to preview a single card without having to actually select it. Therefore, whenever a user hovers the mouse pointer over a card, the application increases the opacity of that card until it appears solid. You can achieve this result via a combination of event handlers and animations.
Specifically, the application employs two animations: one to increase the card’s opacity and another to reduce the opacity when it is no longer being hovered over. These animations are started by card’s MouseEnter and MouseLeave events, respectively:
protected void frontSide_MouseEnter( object sender, MouseEventArgs e) { // Start the animation that raises the // opacity of the card. _animShowCard.Begin(); _control.SetValue( Canvas.ZIndexProperty, 99); } protected void frontSide_MouseLeave( object sender, EventArgs e) { // Start the animation that lowers the opacity of the card. _animHideCard.Begin(); _control.SetValue( Canvas.ZIndexProperty, ZIndex); }
To start an animation, you simply get a reference to the appropriate StoryBoard object and then call the Begin method. (Storyboard objects also bubble up notification events when an animation begins or completes.)
There are two different ways to store animations: inline or as resources. The user viewer application stores animations as resources. Note that you must control animations stored as resources from code as opposed to markup.
![]() | |
Figure 2. Enlarged Card: When clicked, a “fly-out” animation enlarges the card, providing additional screen space for detail information. |
Viewing Cards
The user viewer also uses animations to control the fly-out and fly-in effects when a user selects a card. While the minimized/rolodex view is sufficient for selecting a card, you want it to enlarge when a user clicks on it. The enlargement provides a “master/detail” effect because enlarging the card increases the space available for displaying user information (see Figure 2).
The left button click event handler begins the card fly-out animation that enlarges the card and shifts it to the right. The application hooks the fly-out animation’s Completed event, at which point you can can unsubscribe from the card’s mouseover and left mouse button click events, because you don’t want to react to those events when a card is enlarged. Instead, you want to hook a different event handler up to the left mouse button click event of the user card. This new event handler starts an animation that shrinks the user card and shifts it to the left, moving it back to its original position.
Editing Card Data
Out of the box, Silverlight includes only the most basic controls such as Rectangles, TextBlocks, and Canvases—meaning it has no built-in input functionality. As you can imagine, writing complex controls such as TextBoxes and ListBoxes yourself, from scratch, would be quite an undertaking. To circumvent that problem, Silverlight provides rich interaction with the HTML Document Object Model (DOM), giving you the ability to interact both with HTML elements already on the page, and with HTML elements you create and add to the page dynamically at run-time using the HTML DOM.
When creating a Silverlight host control you obviously need to specify certain common properties such as width, height, and version, but you also need to set some other properties to control specific aspects of Silverlight’s behavior when interacting with the host HTML page and the HTML DOM. If you need to overlay HTML controls on top of the Silverlight host control you must specify a value of true for the isWindowless property. However, you should set isWindowless to true only when you truly need that functionality, because there are performance implications when running in windowless mode:
Silverlight.createObjectEx({ source: "Page.xaml", parentElement: document.getElementById( "SilverlightControlHost"), id: "SilverlightControl", properties: { width: "100%", height: "100%", version: "1.1", enableHtmlAccess: "true", isWindowless: "true" }, events: {} });
![]() | |
Figure 3. Overlaid HTML Controls: You can see the three editable HTML text input controls that have replaced the read-only Silverlight TextBlock controls on this card. |
You need to set one other property, called EnableHtmlAccess, to true for a Silverlight application to interact with the HTML DOM (see the preceding code for an example). Strictly speaking, that code isn’t necessary—the default value for this property is true, so you only need to provide a value if you need to disallow access to the DOM from Silverlight. Setting this property to false in the user viewer application would cause it to throw a run-time error stating that HTML access is restricted, and the page would fail to render.
Because Silverlight doesn’t provide editable widgets, the user viewer application supplies editing functionality by replacing Silverlight TextBlock controls containing username, email, and messenger information with HTML text input controls as shown in Figure 3. After you have a reference to an HTML control, you can then manipulate it just as you would in JavaScript:
HtmlElement txtEmailInput = HtmlPage.Document.GetElementByID("txtEmail"); // Set value. txtEmailInput.SetAttribute( "value", _txtEmail.Text); // Set style. txtEmailInput.SetStyleAttribute( "display", "block"); txtEmailInput.SetStyleAttribute( "position", "fixed");
Through the HtmlElement reference, you can read or modify a control’s attributes and style attributes. Attributes represent properties such as input type and value, while style attributes represent anything that would be contained within the control’s style attribute in HTML. Because processing happens in the same context on the client as the hosting HTML page, when a user updates the contents of an HTML control, that update is immediately available to your managed code. To get data out of the HTML controls, you just read the value attribute; in this case you want to update the TextBlock values before minimizing the edited card, transferring any user edits back to the original card:
string email = HtmlPage.Document.GetElementByID( "txtEmail").GetAttribute("value"); _txtEmail.Text = email;
Possible Enhancements
While the sample user view application is functionally feature complete, there are certainly ways to update the application and take it to the next level. Hooking the data retrieval and updating functionality into the ASP.NET membership system would be a great next step. The application could then be used to manage user attributes as well as manage user passwords and roles.
One thing Silverlight does really well is manage use of screen real estate. You could enhance the user viewer to take further advantage of this strong suit. Imagine an application such as this for managing all the data associated with an ASP.NET membership system user. You could display data such as name, username, and email on the front of the card, and use an animation to flip the card over and expose a role management UI on the back side. With Silverlight, the opportunities are wide open.