Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

UserControls as Screens in Silverlight 1.1 - Part 2 of 2

Pete Brown - 16 October 2007

In Part 1, I outlined how we handled screen management in the Silverlight 1.1 Carbon Calculator. In Part 2, I'll present a basic framework for handling similar screen management in your own Silverlight applications. If you haven't yet read Part 1, please do so, as it presents the basics we're building upon here.

Links for the demo application and the source code are both posted at the end of this article. For all images, click the thumbnails for larger versions.

The pattern illustrated here provides a way to simulate windows in what is inherently a non-windowed system: the Silverlight plug-in running inside the browser. While I may toss around words like framework, what I have here isn't necessarily a framework to be used as-is, but it is an implementation of a pattern you may find useful in your own Silverlight applications. Feel free to take the code and adjust as you see fit.

Before we start, if you have Silverlight 1.1 installed, you may want to run the demo first, to get a feel for how it operates. You can run the demo from my site here

 

Basics

As is typical in a Silverlight application, it all starts with Page.xaml. Atypically, however, Page does next to nothing in this application. Instead, Page instantiates MainScreen which is the first screen in the application to follow the UserControl-as-screen pattern. The downside of that is the vast majority of the application has to resolve control names using FindName, but I explained some ways to ease that pain in Part 1.

MainScreen hosts on it a couple buttons and some hidden screens. Those screens are shown or hidden based upon which buttons the user clicks. While I could have put those all directly on Page, having a MainScreen like I do here allows me to animate the main screen the same way I do all the others.

Unlike a true windowing system, everything here exists in the same screen space, so you have to understand zorder and the natural placement of xaml elements so that, for example, your dialog box doesn't display underneath the caption for something on your main screen. Luckily, this is pretty simple to do as long as you don't muck around with z-order early in the process. Simply put things in the order they should be on the visual stack with the items most on top listed last in the xaml file (the xaml processor puts them in in the order it reads them so last=on top)

If you do mess with z-order, do so understanding you'll probably lose a lot of hair in the process. At least in the earlier versions of 1.1, z-order didn't work quite correctly, and it caused all sorts of issues. My main recommendation here is to establish a range of zorders to be used for each "layer" in the application, and stick to those numbers; that will make it easier for you to manage.

Class Model

Since I wrote this demo from scratch, I took the opportunity to refactor a bit and use more intuitive (no baggage) class names. The class diagram follows below.

 

Whereas Part 1 presented ControlBaseEx for the primary base class, I simplified that here to ControlBase. I also refactored the wizard functionality into a separate set of classes rather than roll it all into the main screen base class. In the end, I think you'll find the code easier to follow. While the code is pretty self-explanitory, here are the descriptions of the main classes:

ControlBase

This is the root class from which all my controls and screens derive. It has three things we're interested in:

LoadXaml() - This function wraps the ugliness that comes with the usercontrol template by default. It also handles assigning the RootElement needed for FindByName

RootElement - This is the element returned from InitializeFromXaml. You need this to do any FindName-type name resolution as calling FindName on the control itself will not return what you are looking for. (it took me a while to figure this out on the Carbon Calculator project)

FindByName - As mentioned in Part 1, FindByName cleans up the FindName functionality a bit, automatically going agains the correct root element and returning a strongly-typed reference to the element.

ScreenBase 

The primary class upon which the basic screens build is ScreenBase. ScreenBase includes a number of useful functions with Show() and Hide() being the most important.

Show() handles displaying a window on the screen. Since this calls out to an animation with a known name (the constant in this file), the way the window is shown can be almost anything from a simple display to rolling in from the side of the screen while zooming in and out and varying opacity. Of course, that may make the user cry out for Dramamine and ginger root, but I'm not concerned about that here :)

Hide() does the opposite of show. Like Show, if an animation with a known name exists, if will use that to hide the screen. Otherwise, it will just set the screen to Visiblity.Collapsed.

 

Basic Screens

Basic screens, like MainScreen.* are the foundation upon which we build these types of applications. While I used one only for the root screen here, one could use them for any number of other screens in a typical application.

Keep in mind that screens need not be rectangular. You could use this pattern to implement simple scene management functionality to keep the application components encapsulated when using very fluid screens with complately non-rectangular shapes.

 

Wizard Pages

Like most people with a Windows Forms (and VB3-6) background, I have have written countless wizards over the years. For this Silverlight example, I whipped up a very simplified version of the pattern I followed in those wizards.

The wizard itself controls the navigation from page to page. Each page, however, controls whether or not it allows moving forward or moving backwards, typically based upon its position in the chain and whether or not the data entered on that page is valid.

When you get into more complex wizards (with multiple brances, and the ability to skip around based on user entries) I'll typically have a navigation graph/tree at the controller page level and handle all navigation through it. In that case, each page will simply implement an IsValid method to let you know if the page data is ok or not.

When storing data for the wizards, a publically-accessible class (typically a singleton) is the easiest way. Each page of the wizard will then plug its values into the wizard pages.

In the carbon calculator, we did basically that. Steve wrote the data and calculation classes and I wrote the singleton state manager which maintained the application state. The wizards simply get/set data in those classes via the state manager.

There are lots of different ways to handle wizard management. Feel free to implement your own patterns directly on top of the usercontrol screen framework presented here.

 

Modal Windows

Modal windows (dialog and message boxes in standard Windows applications) are a special type of window which blocks all other processing until the user takes an action to close the window. To simulate the most important part of this behavior: disallowing clicking on anything outside of this window, I use a screen-covering canvas to absorb the clicks and visually darken the rest of the Silverlight control. Ideally you'll make your covering some variant on the primary background color from your main screen. In the Carbon Calculator, that was white; in this example, it is black.

 

Hey, dig those enormous touchscreen-friendly buttons :)

The "modal" window here has a DialogResult just like real modal windows. Interestingly enough, Microsoft left the DialogResult enum in System.Windows.Controls for Silverlight. I simply re-used it here.

Like the other windows, one simply uses the .Show() method to display the dialog. Unlike standard Windows modal dialogs, the .Show() method immediately returns, so you need an event handler to capture the dialog result once the user closes the window. The code you use to retrieve the dialog result looks like this:

   // Raised when the dialog hide animation has completed
   void _exampleDialogScreen_Closed(object sender, EventArgs e)
   {
       // Operate on the selection made in that window
       if (_exampleDialogScreen.DialogResult == DialogResult.OK)
       {
           _messages.Text = "You clicked \"OK\"";
       }
       else
       {
           _messages.Text = "You cancelled the dialog.";
       }
   }

Debugging Tip

One main debugging tip to offer you when you build your own extensions to this: put exception handlers in your constructors. I usually put exception handlers in there with a break point on the catch statement (or you can set the compiler to break on all exceptions). The reason is that exceptions in the usercontrol constructors show up as XAML Parser errors further up the stack. The deeper you nest your screens and controls, the harder it will be for you to find problems.

 

Wrap-Up

You can run the demo from my site here

The Silverlight 1.1 September Refresh version of the source code may be found here. You'll need Visual Studio 2008 Beta 2 to compile it. In the code I have included a sample button, a sample dialog box, and a sample wizard. The base classes are all also present.

There is a lot more that you can do with this pattern to make a true and robust windowing system in Silverlight, especially around handling modal windows and handling reuse/dynamic instantiation of multiple instances of a single window. If Microsoft doesn't provide a windowing system in the release of 1.1 (most RIAs can function well without one), I'll revisit this project and make it into something serious.

If you have any questions or ideas for this, please post a comment right to this post. I'd love to get other ideas on how you might be interested in using a pattern such as this.

       
posted by Pete Brown on Tuesday, October 16, 2007
filed under:        

34 comments for “UserControls as Screens in Silverlight 1.1 - Part 2 of 2 ”

  1. Emari2says:
    Marktap sent me here from
    http://silverlight.net/forums/p/5903/18073.aspx#18073
    I am impressed!. For years I have been programming in visual basic with MDI for everything and I tried to do the same with Xaml+Silverlight (well, to see if it was possible, so my questions in the forums).
    I need to update my mind. Your information is genial.I will do a hard effort to translate all your C# to Basic (.net as the last)as it was my language since 1981 with my first Sharp MZ80 computer.Keep on going. I guess I need time even when I do not have all that but I'll follow your lessons.How do you fit Blend in all of this?. Thanks
  2. Pete Brownsays:
    Hi Emari2

    I presented on the usercontrol as screens pattern back in 1999 at a Microsoft conference in DC. The language at the time was VB5/6. If I can find the samples from way back then, I'll let you know. The pattern is the same although it would be interesting to see what I have unknowingly changed since then.
  3. Emari2says:
    I have an error
    Protected Sub LoadXaml(ByVal xamlResourceName As String)
    Try

    Dim s As System.IO.Stream = Me.GetType().Assembly.GetManifestResourceStream(xamlResourceName)
    Stop
    _rootElement = Me.InitializeFromXaml(New System.IO.StreamReader(s).ReadToEnd())

    Catch ex As Exception
    Stop
    System.Diagnostics.Debug.WriteLine(ex.ToString())
    Throw
    End Try

    End Sub
    in _rootelement=Me.nitializeFromXaml
    that supose do not let to show the mainstreen page
  4. Pete Brownsays:
    Hi Emari2

    I see this is your translated version of the code, so it could be anything.

    However, an error in InitializeFromXaml typically means either an exception was thrown from the constructor of an object that was on that usercontrol (put try/catch in each constructor and a breakpoint in the catch to see the exception, or just break on all errors), or the xaml itself was bad. Since you are translating the code from C# to VB, I'm going to assume the former.
  5. Emari2 (Emilio)says:
    Hi,
    I arrive well to this subrutine
    protected void LoadXaml(string xamlResourceName)
    …….
    When I execute your code I found the following
    Value of s={System.IO.UnmanedMemoryStream}
    Value of xamlResourceName=”PeteBrown.UsercontrolScreens.Screen.Mainscreen.Xaml”

    When I execute my code
    Dim s as System.IO.Stream=Me.GetType.Assembly.GetManifestResourceStream(xamlResourceName)
    Value of s = nothing
    Value of xamlResourceName=”PeteBrown.UsercontrolScreens.Mainscreen.Xaml”
    (I did not make the screen folder)
    Unfortunately I do not know how to get the right value of s in Visual Basic.
    Note. I needed to change the Mainstreen() for New() to get the LoadXaml.
  6. Pete Brownsays:
    I think the main problem you're running into is VB's (annoying, IMHO) method of dealing with the namespace. In your project property pages, there will be a property named something like "Root Namespace" and will have the full project name stuffed in there. Make sure that is only the root name.

    Then, change the namespace declarations in code to only have the additional part (for example just "Screens" instead of "PeteBrown.UserControlScreens.Screens" assuming the root namespace is set to "PeteBrown.UserControlScreens".

    If that doesn't work, send me the latest version of your translated code and I'll take a stab at it. I should have some time later this evening.

    Pete
  7. Rachida Dukessays:
    I'm very excited about silverlight 1.1, and very eager to learn it. Your sample is nice sample to get started. I look atUserControls as Screens in Silverlight 1.1 (Part 1 and 2).Can you give any more information about the follwing code :
    <:MainScreen X:Name="MainScreen" Canvas.Top="0" Canvas......
    I opened your sample in expression blend 2 preview september.
    Thanks,
    Rachida Dukes
  8. Pete Brownsays:
    Hi Rachida

    I'm not sure exactly what you're asking. However, that line in the xaml is what places my MainScreen control on the canvas.

    The prefix in front of MainScreen (missing from your post) is set at the top of the xaml file. That's how Silverlight knows what library to use to validate my control's xaml representation.

    Canvas.Top and Canvas.Left are attached properties (attached by the Canvas) that indicate where the control should be placed.

    MainScreen is the name of the class (usercontrol). "MainScreen" is also what I named that instance. You could call it any valid name you would like. "mainScreen" (camel case rather than pascal case) being more compliant with xml norms.

    Hope that helps.

    Pete
  9. Pete Brownsays:
    Hi Susan

    I assume you're using VS 2008 RTM (the one that was just released). If so, the Silverlight add-in from Microsoft hasn't yet been updated to support that version. To do Silverlight work, you need to continue to use Beta 2.

    If you have Beta 2 and couldn't open it, go to www.silverlight.net and under "Getting Started" you'll find the link to the Silverlight tools for Visual Studio. Download and install those, then you can open the project.

    Hope that helps.

    Pete
  10. Robertsays:
    I get the following warning when running the downloaded code (VS 2008 Professional release):

    The element 'Canvas' in namespace 'http://schemas.microsoft.com/client/2007' has invalid child element 'MainScreen' in namespace 'clr-namespace:PeteBrown.UserControlScreens.Screens;assembly=ClientBin/PeteBrown.UserControlScreens.dll'. List of possible elements expected: 'Code' in namespace 'http://schemas.microsoft.com/winfx/2006/xaml' as well as....

    The project does appear to work as it should, but I would like to understand why this is a warning. Thanks for a really great sample to learn from!

  11. Pete Brownsays:
    Robert, as I recall, that's because the Silverlight XAML editor in VS2008 is based just on an XSD schema instead of any type of true intellisense. The XSD doesn't dynamically change to account for additions to the schema (usercontrols, for example)

    I would expect that to change prior to release.

    Pete
  12. Danielsays:
    Hey pete!

    I made also my own controls, but there are not so advanced :) . Ok , i have a BIG question. I don't understand how you add this:

    <s:MainScreen x:Name="MainScreen" ...> widht "s" prefix :)
    also
    <c:TreeView x:Name="tree" ...

    <c:AnimatedFlowPanel x:Name="thumbnails" ...

    I know that mainScreen are Classes but i try to put in my own project, and, not successiful :)

    You may send me a link, i don't found:) Thank you, and sorry for my english :)
  13. PNsays:
    Hi Pete,

    I have found your User controls demo and code very useful and i building my SIlverlight APplication by taking inputs from the Code.

    I have some queries in your code:
    Suppose I have a button on WizradPage01 [Not on WizardController screen] and i want to Open the WizardPage02 on click of this button, how can i do this. I am not able to get the _pages List of WizardController in WizardPage01.

    Also if i want to pass value from WizardPage01 to WizardPage02, how can get this?

    Thanks in Advance,
    Parimal
  14. Pete Brownsays:
    Hi PN

    I'd need to go back through the source to give a real answer on that. In general, in wizards, you want to set an option (which may change the navigation so that some other page would come next) and then take all navigation via next/previous. It can get confusing to track the order of items otherwise. If you need to do a bunch of that, you probably want something other than a wizard model.

    If you had to do it (and it wasn't just a modal dialog popped-up from the page) then you could set up a set of navigation events which each page could raise to tell the controller page which page to go to.

    That all being said, I'll have a Silverlight 2 version of this out after Silverlight 2 Beta 1 is released.

    Pete
  15. Pete Brownsays:
    Hi PN.

    There's an inheritance issue in Beta 1 that changes my approach, at least temporarily. I need to find a reasonable way to work around that before I can convert this, as I relied heavily on inheritance.

    One tidbit: use the Popup control for the dialog box that opens up.

    Pete
  16. PNsays:
    Hi Pete,

    Thanks for the response.

    As inheritence is not possible, is it a good idea to put all the pages [In our solution wizardpage01,wizardpage02 andwizardpage03] on single page/usercontrol as different canvas and then based on next/previous button values on that page hide and show the pages.

    Is this approach fine? If you got any other solution let me know.

    Thanks in Advance,
    Parimal
  17. Pete Brownsays:
    Hi Nagina

    With a bunch of work, you can simulate this behavior in SL1. You'll need to follow the OO/control pattern for Silverlight 1.0 applications and use createFromXaml to load in the xaml fragments for the pages.

    It's much easier in SL1.1/SL2, however.

    Pete
  18. Mike Broughtonsays:
    Pete,

    Have you found a solution for beta2 and your usercontrols? We heavily utilised your principles in mocking up our project, only to be thwarted now by the latest release!

    Many thanks,

    Mike
  19. http://pressreleaseww.comsays:
    It is selective about which section of each layer is assembled, similar to DMLS or SLS 3D printing, but at the same time SLA
    printing belongs in its own genre because it creates
    products based off of a curable resin and a beam of ultraviolet light.
    Working with a wide range of materials, Croft Filters skilled team of experts offer first class consultancy on any aspect of mesh filters, filter elements and filter housings to help meet
    customers individual filtration requirements. Applications for renewal must be made
    at least 18 months before the expiry date.
  20. dremel 3d printer amazonsays:
    It's not clear whether 3-D printed guns would be the population's best defense
    against tyranny or a frightening new source of tyranny itself.
    Romney has seen the largest gain in Ohio, a state the candidates have bounced around in over the last few months.
    SLA requires placing any light-sensitive liquid polymer in a vat of photosensitive resin.
  21. Carpet steam cleaner under $100says:
    But with a commercial cleaning business because the service you provide
    is needed every day, week, month and year - it's a recurring monetization model.
    I didn't believe in the beginning either, but when you think about it, it's not that strange - celebrities are ordinary people, but more famous than us.
    I hope this article has shined some light on the opportunities
    that a carpet cleaning business can provide you.
  22. 3d printing machinesays:
    It is important to know that amorphous silica is different
    from crystalline silica. Dark Sunsation contains the ingredient DHA, which as explained earlier is safe for the skin. There is strong evidence to suggest
    that it has caused blindness and even death when consumed in quantity.
  23. Vernasays:
    You could hire professionals, but they can charge hundreds of dollars.
    As with every multi-site contract, however, there will be unique
    stores that the bundle matrix does not fit. Domestic Windows Auckland to clean interior and exterior windows - whether its bounce cleaning, a special occasion or regular cleaning practice
    to protect your investment, the experts and environmentally friendly products
    and utensils showed you results.
  24. ilkee.comsays:
    Wear it with leggings or jeans for a casual yet stylish look.

    You have plenty of other accessories and options to choose from to
    make this the perfect gift, like the drop seat pajamas or the hoodie footie pajamas for those that want to be covered head to
    toe. Ryan gave them as funny gifts to his buddies and family.

Comment on this Post

Remember me

2 trackbacks for “UserControls as Screens in Silverlight 1.1 - Part 2 of 2 ”

  1. Christopher Steensays:
    Modular schemas, include, and tooling support [Via: Anil John ] The Seven Deadly Sins of SOA [Via:...