Even a chimp can write code

Sunday, June 08, 2008

Pages in Silverlight

Pages as a paradigm likely pre-date the web itself although they have been popularized by document-based markup languages deriving from SGML. Windows Presentation Foundation (WPF) with XAML further codifies Pages as a first-class UI element from its heretofore usage as a logical element. Looking back from Silverlight, this is as good a point in history to start as any. WPF has built-in support for reusable pages in its applications, and the task of navigation between them. It provides the infrastructure for declarative navigation via hyperlinks or programmatic navigation via NavigationService, and a journal that remembers which pages were navigated to or from. For XBAPs on IE7+, WPF supports integration of the journal with the browser's Back and Forward buttons, while elsewhere it displays a substitute navigation bar with this functionality (the limitation is an effect of how the XBAP host plugs into the browser). There is also a building block called PageFunction which introduces a paradigm for invoking pages as if they were methods, providing a neat little way to build wizard UI. Unlike WPF, Silverlight does not support these things, as of version 2. This post isn't about brooding that absence though -- instead we'll look at the lay of the land in Silverlight 2 with regard to pages and navigation, and look at common workarounds.

Although Silverlight does not have the Page type at this time, the term is currently used for its root visual UserControl. That root visual object is analogous to the root window in WPF, can only be set once for the lifetime of the app, and is effective once the Application's Startup event is raised. The act of navigating from one page to another is similar in principle to the act of removing one child element from an invisible root and adding another child in its place. [Actually given the journal it is more like switching Z-indices between siblings, so that one gets prominence while the other is made inconspicuous]. You can use this same principle in devising the notion of paging in a Silverlight app.


This recipe requires the following ingredients:

  • App.xaml.cs: the code-behind for the Application

  • Frame.cs: a user control which will serve as our "navigation frame". This has no UI.

  • Page1.xaml/Page1.xaml.cs: a user control representing a unique "page". This has a button whose click event navigates away to Page 2.

  • Page2.xaml/Page2.xaml.cs: similar to Page 1 in logic and function, only this represents the second page in your app.

Step 1: Create a navigation frame

This is a really simple user control. It has a public Navigate method which is passed a "page" object for which it duly resets its original content.

// Contents of Frame.cs
public class NavigationFrame : UserControl {

// Navigate the frame to the specified content
public void Navigate(UIElement content)
{
// the existing content of this user control is
// discarded and the specified param is plugged in
// its place
this.Content = content;
}
}

// Optional abstraction for page config
public static class Pages
{
// for simplicity we're using static properties,
// but this could just as easily be a URI to
// Type mapping table
public static UserControl HOME_PAGE = new Page1();
public static UserControl ANOTHER_PAGE = new Page2();
}


Step 2: Wire up the navigation frame as the app's RootVisual

Ordinarily the code generated by Silverlight tools (Visual Studio or Blend) will have a user control hooked up as Application.RootVisual within the Startup event handler. You will change this to hook up the navigation frame. Then hook up the user control to be the navigation frame's content (by invoking the NavigationFrame.Navigate method you created above).

// Contents of App.xaml.cs

// the Startup event handler sets the root visual
private void App_Startup(object sender, StartupEventArgs e)
{
// Navigate to home page
Navigate(Pages.HOME_PAGE);
}

public void Navigate(UIElement content)
{
// Create frame on an as-needed basis
if (this.frame == null)
{
this.frame = new NavigationFrame();
this.RootVisual = this.frame;
}

// Navigate to content
this.frame.Navigate(content);

}


Step 3: Build your pages

Now that the navigation infrastructure is ready, go ahead and create a couple pages. These are merely user controls which would otherwise have been hooked up directly as the RootVisual. That part is simple. An important attribute in this equation is the fact that pages need to provide the experience of "navigating away". You can use a hyperlink, a button, or trap mouse/key events. We'll use a button and insert the navigation logic in it's click event handler.


// Contents of Page1.xaml.cs

// on btn click, this navigates to the second page
private void navigateButton_Click(object sender,
RoutedEventArgs e)
{
(Application.Current as App).Navigate(Pages.ANOTHER_PAGE);
}


Let's create another user control which will be navigated to.


// Contents of Page2.xaml.cs

// on btn click, this navigates to the first page
private void navigateButton_Click(object sender,
RoutedEventArgs e)
{
(Application.Current as App).Navigate(Pages.HOME_PAGE);
}


Well, that's all there is to it really. Build and run, and you've got your simple little navigation application.

In this pattern, the navigation frame element:


  • is the equivalent of the implicit navigation control

  • is a container with no visible visual properties (e.g. Background, etc.)

  • has only a Content element representing the current page


In the pattern, the page element:


  • is the equivalent of the navigable Page

  • has visuals or contains visual elements

  • can be attached and detached from the tree on a navigate event (i.e. the current element is removed, and a new one added in its place)

Our example used the Navigate By Object approach. Note that we invoked the Navigate method and passed it a page object. You could tweak the logic so your pages can be given data as part of the Navigate call. You could just as easily add URI-to-type mapping into the Pages utility type. That would allow you to replicate the Navigate By URI approach which is more organic to content on the web. If you use the Navigate By Object approach and you have a journal that maintains a back/forward stack of pages you've previously navigated to, you can incur a working set hit. This is because your journal will retain references the page objects in memory for long intervals - usually the life of the app - preventing the garbage collector from reclaiming them. Using the Navigate By URI approach in such situations can offer better use of memory. This is because you can delay instantiation of page objects until when the Navigate needs to happen.


In closing, there is a good case for pages, navigation service, journalling, transition animations, deep linking etc. to be part of the Silverlight platform. This is certainly of interest to us as we look toward the next version of the runtime and it's framework. I'd like your thoughts and feedback on these features and how important they are to your scenarios.


Further reading:


PS: For Ben. Sorry this has been long overdue.

PPS: Thanks to Michael Weinhardt for valuable insights.


Update [06/16/2008]: Dave has a new post on navigation with transition effects. Link under the Further Reading section above.

Labels: , , ,

Email this | Bookmark this

How to tell a Silverlight assembly from a .NET Framework one?

Silverlight 2 application code is compiled to Common IL and packaged into assemblies (.dll) which are in turn deflated into a Zip file (container with .xap extension). The IL is a CPU- and platform- independent instruction set. The Silverlight runtime has in it the Core CLR which executes this IL. Given that .NET Framework involves much of the same things save for the .xap packaging, how does one tell between a DLL built for Silverlight versus one for .NET Framework?

Well, the answer is in one of the security assumptions that Silverlight's Core CLR makes to distinguish a core platform assembly from a transparent code app assembly. The mscorlib.dll in Silverlight is signed with a different key than it's namesake in .NET Framework. This is apparent in it's full name, or specifically it's public key token.

The public key token for mscorlib in Silverlight is:

7cec85d7bea7798e

while the public key token for mscorlib in .NET Framework is:

b77a5c561934e089

So the trick to sniffing out an arbitrary managed code assembly for its allegiance - Silverlight or .NET Framework - is to look through it's list of referenced assemblies, locate mscorlib and then check the public key token. Here is a sample application that does that.

Labels: ,

Email this | Bookmark this

Thursday, June 05, 2008

Beta 2 of Silverlight 2 has just shipped

The second beta of Silverlight 2 has just been released. The team has been cranking away and there are some new features and lots of bug fixes to show. A big thanks to all who provided vital feedback on our previous pre-releases!

  • Animation
    1. Support for animating custom data points
    2. Object Animation support (animating structs)
  • Deep Zoom
    1. New XML-based file format
    2. MultiScaleTileSource to wire up your own images and get the Deep Zoom experience
    3. Better notifications when sub-images enter the view
  • Controls
    1. Customize the look and feel of controls using Visual State Manager. Interactive control templates were never so easy.
    2. Some base controls are now part of the core platform, rather than packaged into apps. Say hello to smaller app sizes.
    3. Calendar now supports multi-selection and blackout dates
    4. New TabControl
    5. Control properties changes (Background, Tooltip, FontFamily, FontSize…)
    6. DataGrid improvements: auto size, reorder, sort, performance and more
  • TextBox
    1. IME Level 3 input support
    2. Text wrapping and multiline selection highlighting in textbox
    3. Scrolling and clipboard support
    4. Document level navigation keys
  • Improvements in error handling, reporting
  • Parser and Property system
    1. DependencyProperty.Register/RegisterAttached now supports PropertyMetadata
    2. New DependencyObject.ClearValue
    3. Visual tree helper
  • Data-binding
    1. Per binding level validation
    2. Support for element syntax for binding markup extensions
    3. Binding to attached properties
    4. ItemsControl extensibility (OnItemsChanged method)
    5. Fallback in value conversion (Binding.UnsetValue)
  • Input
    1. Support for limited keyboard input in Full Screen mode (arrow, tab, enter, home, end, pageup/pagedown, space). I've seen more than a few requests for this on the forums.
    2. Managed APIs for Inking and stylus input
  • Networking and Data
    1. Cross Domain support in Sockets
    2. Cross Domain security enhancements
    3. HttpWebRequest and WebClient callable from background threads
    4. Upload support in WebClient
    5. Isolated Storage: default size increased to 1MB and new ability to change quota with user consent. Also a new management UI.
    6. Duplex communications ("push" from server to Silverlight client with no need to "poll" for data)
    7. LINQ -to- JSON serialization
    8. Significantly improved SOAP interop
    9. "Add New Item" template in Visual Studio for "Silverlight-enabled WCF Service"
    10. ADO.NET Data Services support
  • UIAutomation and Accessibility support in the platform
  • Media
    1. Platform support for Adaptive Streaming (also referred to by people as multi bitrate), the ability for Silverlight to switch between media depending on network and CPU conditions
    2. Content protection with PlayReady DRM and Windows DRM
    3. Basic server-side playlist (SSPL) support
  • Localization
    1. Changes to localized application model. You now create one xap per locale, instead of one monolithic multilingual app
    2. Expanded localization languages of runtime and SDK
    3. Japanese SDK Installer and documentation (July 10)
  • Several changes to make API and behavior more compatible with WPF
  • Tools
    1. Beta 1 projects will be automatically ported to Beta 2
    2. Remote debugging for VB on the Mac
  • CLR
    1. A new developer-oriented runtime package with debugging binaries, localized strings, docs etc.
    2. Support for OS fallback logic for resources
    3. CurrentCulture and CurrentUICulture isolation to AppDomain level
  • DLR
    1. Performance improvements
    2. Various new DLR and IronPython 2.0 Beta 2 language features
    3. Various new IronRuby features

We're not quite done for Silverlight 2, but are getting really close. There's some ongoing work to get more controls delivered (Scott has said the expectation was to ship over 100 controls) at and beyond RTW. I know people have asked for ComboBox and PasswordBox. We'll also have per-frame callback support (CompositionTarget.Rendering event) to light up those physics animation scenarios you're dreaming of, and further accessibility work on controls, improvements to PNG support in imaging, among other things. Needless to say, the focus from here on will be to stabilize and ship a solid product.

I hope you like what we've cooked. Please keep that feedback coming. Without you this would not be possible and be quite meaningless.

Further content:

Labels:

Email this | Bookmark this