Welcome to irritatedVowel.com Sign in | Help

POKE 53280,0: Pete Brown's Blog

Silverlight, WPF, Woodworking, .NET Programming, CNC, Nature, and other topics.

Pete Brown writes on a number of topics including Silverlight, WPF, .NET, woodworking and working as a consultant in the DC area. On most forums, Pete goes by the name Psychlist1972. Pete has worked at Applied Information Sciences (AIS) since 1996 where he currently performs as a lead architect and project manager.

Subscribe to my feed

Add to Technorati Favorites
Applied Information Sciences - My Employer

Community Events



World Domination

who's online

Networks


View Pete Brown's profile on LinkedIn

AddThis Social Bookmark Button

UserControls as Screens in Silverlight 1.1 - Part 2 of 2

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.

  Add to Technorati Favorites
Posted: Tuesday, October 16, 2007 6:47 PM by Pete.Brown

Comments

Christopher Steen said:

Link Listing - October 17, 2007
# October 18, 2007 4:02 AM

Christopher Steen said:

Modular schemas, include, and tooling support [Via: Anil John ] The Seven Deadly Sins of SOA [Via:...
# October 18, 2007 4:04 AM

Emari2 said:

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
# October 21, 2007 9:07 AM

Pete.Brown said:

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.

# October 21, 2007 12:22 PM

Emari2 said:

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
# October 22, 2007 4:31 PM

Pete.Brown said:

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.

# October 22, 2007 5:30 PM

Emari2 (Emilio) said:

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.
# October 23, 2007 3:26 PM

Pete.Brown said:

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

# October 23, 2007 3:57 PM

Rachida Dukes said:

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
# November 14, 2007 1:30 PM

Pete.Brown said:

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

# November 14, 2007 1:37 PM

Susan said:

Hi, I downloaded the file and tried to open the solution file with Visual Studio 2008, but it returns "The project file cannot be loaded." Any idea why? Thanks, Susan
# November 20, 2007 2:57 PM

Pete.Brown said:

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

# November 20, 2007 5:28 PM

Susan said:

I've downloaded and installed this: http://go.microsoft.com/fwlink/?LinkID=89147&clcid=0x409 but it hasn't changed anything.. I'm not sure which tool you are talking about?
# November 22, 2007 1:26 PM

Robert said:

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!
# December 8, 2007 3:26 PM

Pete.Brown said:

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

# December 10, 2007 8:20 AM

Pete.Brown said:

Hi Susan I'm not sure what is going on with the install on your machine. How about posting the question over on the Silverlight Forums in the Getting Started section? http://silverlight.net/forums/14.aspx Pete
# December 10, 2007 8:25 AM

Daniel said:

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: widht "s" prefix :) also
# January 12, 2008 3:53 PM

PN said:

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
# February 19, 2008 6:56 PM

Pete.Brown said:

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

# February 20, 2008 11:15 AM

PN said:

Hi Sir, The SL 2.0 is released. Can you please help me in the above exmaple to be converted to SL2.0 regards, PN
# March 6, 2008 2:36 AM

Pete.Brown said:

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

# March 6, 2008 10:46 AM

PN said:

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
# March 7, 2008 9:17 PM

nagina said:

Hi , I would like to make this wizard using visual studio 2005 and silverlight 1.0 , is it possible?
# March 23, 2008 7:36 AM

Pete.Brown said:

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

# March 23, 2008 2:30 PM

Pete.Brown said:

PN

Yes, that solution will work fine.

Pete

# March 23, 2008 2:30 PM

Mike Broughton said:

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
# August 20, 2008 3:16 AM

Cesar said:

Have you migrate it to SL 2.0 Beta2? It would be great to test it on that version! Thanks a lot! Cesar
# September 21, 2008 1:36 PM

Pete.Brown said:

@Cesar

Sorry, I haven't migrated this to Silverlight yet. I may after RTM.

Pete

# September 23, 2008 2:24 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

Enter the text you see in the image:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS