Ask Kathleen

Display Multiple Pages in Silverlight

Learn how to display multiple pages in Silverlight and discover the tools and controls that ship with it; track down a bug in a case statement; and take advantage of helpful tips for trying to obtain a clean Code Analysis.

Technologies mentioned in this article include Silverlight, Windows Presentation Foundation, Visual Studio Team System, Visual Basic and C#.

Q How can I change forms in Silverlight so my application works in exactly the same way in both my Silverlight and WinForms applications?

A Silverlight Forms correspond roughly to Pages in Silverlight, and each Silverlight application has exactly one Page. So, I'd like to rephrase your question to something like this: "How can I make it seem as though I'm changing forms?" Once you ask the question this way, you're halfway to the answer. You provide a page that acts as a container for user controls, and each user control corresponds to a form in your existing application (see Figure 1). Pages can have only one content element, so you generally use a container such as a grid to hold your user controls. The grid is a nice container to use because it lets you overlay controls when needed; it also lets you include features that behave consistently across all your user controls. This is similar to using a Master Page in ASP.NET.

Q I've heard there's a TreeView control for Silverlight 2.0, but I don't see it in the toolbox. I'm having a hard time figuring out what controls are in Windows Presentation Foundation [WPF] and Silverlight, respectively.

A Microsoft is releasing controls for both WPF and Silverlight as Toolkits. This avoids delaying the release of important tools until a normal ship cycle.

In addition to the controls it ships with, Silverlight has a preview of a Visual State Manager in the main tool, while WPF has a preview in the WPF Toolkit. The Silverlight Toolkit also includes a preview of an Implicit Style Manager that recreates some functionality that's inherently part of WPF.

The Visual State Manager in Silverlight allows you to describe states for controls, and defaults for what the controls should look like when they're in each state. It also allows you to dictate how long the transition into a given visual look should take (the length of the animation). When creating applications, programmers can override the standard state appearance with their own. The Blend tooling for this is extremely good, making it much easier to customize controls than in WPF, where individual styles and triggers are needed to provide this functionality.

The Implicit Style Manager introduced to Silverlight as a preview in the Silverlight Toolkit allows you to apply the style based on type, rather than by requiring an explicit style declaration on each instance of each control. This isn't as automatic as the WPF approach because each control is based on a container that applies styles as a theme, and the container must be placed in each user control. Also, you can't change the theme at runtime. The good news is that the Silverlight Toolkit contains a great set of themes to get you started.

Q I have a bug in a case statement that I don't understand. This code works correctly when the direction is right or below. It throws an InvalidOperationException when None (the default) is passed, which it shouldn't do. Direction is an enum. Can you tell me what's wrong?

Select Case mDirection
   Case Direction.Right
      location = New Point(mPopup.ActualWidth, 0)

      location = New Point(0, -mPopup.ActualHeight)
   Case Else
      Throw New InvalidOperationException()
End Select

A This is an easy mistake to make because Or acts as a bitwise or Boolean operator depending on the operands. In this case, the operands for the Or operator in the line I've highlighted act as a bitwise operator. I'm going to guess that the value of Directions.None is zero, which is a good choice for the default value. If that's true, the result of the Or operation is Direction.Below. This is why it works for everything except the value of Direction.None.

Just use a comma to separate the different values you wish to match:

Case Direction.Below, Direction.None

If you make this mistake in C#, you fix it by using two adjacent switch statements with no break statement:

switch (mDirection)
   case Direction.Right:
       location = new Point(mPopup.ActualWidth, 0);
   case Direction.None:
   case Direction.Below:
         location = new Point(
            0, -mPopup.ActualHeight);

Q I'm trying to get a clean Code Analysis. Unfortunately, the code in one of my classes is giving Code Analysis warnings. My boss says we need to comply fully with Code Analysis without suppressing any messages. The code looks like this:

private void WriteMessage(string message)
   string text = String.Empty;
   if (mOwner.Content != null)
      text = mOwner.Content.ToString();
       text + ":" + message);

The warning is:

CA1801 : Microsoft.Usage : Parameter 'message' of 
'Class1.WriteMessage(string)' is never used. Remove the 
parameter or use it in the method body.

Why does Code Analysis give these errors when the parameter is obviously used?

A Code Analysis analyzes the Release Assembly. The compiler removes the Diagnostics.Debug line when compiling. If you check the IL through Reflector, you'll see that the only code using the parameter and variable doesn't exist within the Release Assembly. Interestingly, the same code in VB gives a second warning:

CA1804 : Microsoft.Performance : 'MenuProvider.WriteMess-
age(String)' declares a variable, 'text', of type 
'String', which is never used or is only assigned to. Use 
this variable or remove it.

There are a few ways to work around these problems, but your boss is making a mistake when he insists on setting a goal for full Code Analysis compliance without suppressing any items. A good solution in this case is to formally ignore the warnings. If you right-click on the warning, you'll have the option of ignoring it in an external file or in the source code. I much prefer suppressing the messages in source code and always including a Justification. This lets other coders know why you decided to ignore the warning.

You can't just remove the code with conditional compilation because other code is calling the method. A slightly more complex approach would be to alter the class to be a partial class and add a new declaration for a partial method with the same signature (name and parameters) as your WriteMessage method. You don't need a second class declaration; instead, change the existing one to include the keyword partial. Then wrap your actual WriteMessage method entirely in a #if DEBUG directive:

      partial void WriteMessage(string message);

      partial void WriteMessage(string message)
         // Code as above

When you compile this code for debugging, Visual Studio will find the real WriteMessage method, and the method will work as expected. When you compile for release, the DEBUG constant will be missing, so your implementation of WriteMessage will be missing. Due to the partial class declaration, the compiler will remove all calls to your WriteMessage method.

This only works because your method is private and void. It also provides a slight runtime performance improvement because calls won't be made to the method, and the string concatenation won't be performed. This performance improvement will be small, but can still prove significant if you call this method a number of times.

Q I'm trying to get a clean Code Analysis run for some Visual Basic code, but I'm getting the following warnings:

Warning 3 CA1034 : Microsoft.Design : Do not nest type 'Menu-
Item.MenuItemClick2EventHandler'. Alternatively, change its 
accessibility so that it is not externally visible.
Warning 4 CA1003 : Microsoft.Design : Visual Basic generates 
EventHandler delegates for events that use the following 
pattern: Event MyEvent(ByVal Sender As Object, ByVal e 
as MyEventArgs). Change the event that defines EventHandler 
'MenuItem.MenuItemClick2EventHandler' to use 
EventHandler<T> by defining the event type explicitly, e.g. 
Event MyEvent As EventHandler(Of MyEventArgs).

Warning 4 (and possibly Warning 3) is related to this declaration:

Public Event MenuItemClick(ByVal sender _
   As Object, ByVal e As MenuItemClickEventArgs)

How do I fix this?

A Prior to the introduction of generics in .NET 2.0 (VS 2005), each unique event with a unique set of parameters needed an explicit declaration of a delegate that corresponded to the event signature. By convention, these events were named with the name of the event followed by "EventHandler." If you checked the code in Reflector -- which is still free, but now distributed by Red Gate Software Ltd. -- you'd see a delegate within the class that was generated by the compiler:

Public Delegate Sub _
   MenuItemClickEventHandler( _
   ByVal sender As Object, _
   ByVal e As MenuItemClickEventArgs)

Code Analysis is warning you that there's a nested delegate. This was appropriate for the generated delegate because it limited the potential for naming collisions. If you'd like to quiet the errors, you can use the code suggested by the second error:

Public Event MenuItemClick _
   As EventHandler( _
   Of MenuItemClickEventArgs)

This code uses the generic event handler provided by the framework.

The help entry for this Code Analysis warning says you shouldn't ignore it and that it's a breaking change. This warning holds true for C#, but not for VB. The only downside of this style of event declaration is a few extra lines of code of IL in the assembly, which hardly justifies saying you should never suppress the warning. The automatic usage means changing it isn't a breaking change. You can suppress the second message globally and just ignore warnings about event handlers, but you can't suppress the first globally without missing notification on any other nested public classes. I think it's easier to change your delegate declarations than to explain why you're sticking with the old VB style and managing the suppressions.

About the Author

Kathleen is a consultant, author, trainer and speaker. She’s been a Microsoft MVP for 10 years and is an active member of the INETA Speaker’s Bureau where she receives high marks for her talks. She wrote "Code Generation in Microsoft .NET" (Apress) and often speaks at industry conferences and local user groups around the U.S. Kathleen is the founder and principal of GenDotNet and continues to research code generation and metadata as well as leveraging new technologies springing forth in .NET 3.5. Her passion is helping programmers be smarter in how they develop and consume the range of new technologies, but at the end of the day, she’s a coder writing applications just like you. Reach her at [email protected]

comments powered by Disqus