TUTORIAL #11: Creating a Navigation In Silverlight

A navigation is always a tricky project for every application. It seems to be in a constant state of flux, up until the minute the site goes live. With Silverlight, some of that becomes easier. This post will show you how to create a simple navigation in Silverlight.

Before I start through the steps, I want to point out that the navigation on my site is actually the ONLY part of my site done in Silverlight. The rest of the page is standard ASP.NET. I don’t recommend creating your entire site in Silverlight, but rather, using it where it’s appropriate: like a navigation. OK, having said that, here we go.

1. Create a new project in Visual Studio.

For many of you, this may seem remedial, but I want to make sure that I am documenting all of my steps. There’s something frustrating to me about tutorials that assume you know the first few steps.

Create a new project in Visual Studio.

2. Add the web project it asks you about.

After you create a Silverlight project, Visual Studio will prompt you to create a Web Project to accompany it. Silverlight projects have to be hosted in a web page, so go ahead and create it.

Add the web project it asks you about.

3. Make your XAML document larger.

Open the Page.xaml file in your Silverlight project. By default, your UserControl tag is set to 400 x 300, though I don’t know why that was the dimensions that were picked. IN our case, we’re going to make our navigation 800 x 200. So your XAML should look like this now:

<UserControl x:Class="NavigationSample.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="200">
<
Grid x:Name="LayoutRoot" Background="White">

</
Grid>
</
UserControl>

4. Open your solution in Expression Blend.

I would ALWAYS recommend using Expression Blend for any kind of Silverlight or WPF layout work, because you have the ability to drag and drop your shapes and colors. The next few steps will be shown in Blend.

The first thing I did was open my solution files in Blend.

5. Draw your navigation in Page.xaml.

I am going to be creating a nav similar to the one on my site at http://jeffblankenburg.com.

Before I create anything, I want to change the default Grid control to a Canvas. You can do this simply by changing the work “Grid” to the word “Canvas” in the XAML. Make sure to get the closing /Grid tag too. If you want to understand the differences, I have posts on Canvas, Grid, and StackPanel respectively.

Looking at it, the first thing I want to create is that silverish outer border. That’s simply a rectangle with some rounded edges. I’ve also applied a gradient to it, so it looks a little metallic. Finally, I just made the StrokeThickness = 2. Here’s how it looks in Blend (click to enlarge):

Also, here’s how our entire XAML looks now:

<UserControl x:Class="NavigationSample.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="200">
<
Canvas x:Name="LayoutRoot" Background="White">
<
Rectangle Width="780" Height="100" Stroke="#FF666666" RadiusY="15" RadiusX="15" StrokeThickness="2" Canvas.Top="50" Canvas.Left="11">
<
Rectangle.Fill>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="#FFC4C4C4" Offset="1"/>
<
GradientStop Color="#FFFFFFFF" Offset="0"/>
</
LinearGradientBrush>
</
Rectangle.Fill>
</
Rectangle>
</
Canvas>
</
UserControl>

6. Create the background for the buttons.

There are probably many different ways to approach this problem, so we’ll have to live with my simplified solution. Each button on the navigation is orange, with a subtle gradient. It was created the same way that we created the previous rectangle, so here’s the XAML for it:

 <Rectangle Height="80" Width="760" RadiusY="7" RadiusX="7" StrokeThickness="2" Stroke="#FFB1B1B1" Canvas.Left="21" Canvas.Top="60">
<
Rectangle.Fill>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="#FFF7D599" Offset="1"/>
<
GradientStop Color="#FFE8AF34"/>
</
LinearGradientBrush>
</
Rectangle.Fill>
</
Rectangle>

All we need to do is take that XAML, and place it immediately after the rectangle we’ve already got there. Here’s what your design view should look like now:

7. Position your TextBlocks.

We’ve now reached the step for why I wanted to be using a Canvas as our outer container. We’re going to position each of our text elements, and we’re looking for pixel by pixel accuracy. The Canvas provides us that.

One of the other things you’ll notice about my navigation is that I am not using one of the standard 8 fonts that come with Silverlight. Thankfully, I’ve written a post on embedding fonts in Silverlight as well. Make sure you read that if you want something different than Arial, Times New Roman, or Courier. If you use Comic Sans, call me so I can smack you. 🙂

Anyways, I have 6 navigation elements that I want to create. I am simply using TextBlock controls to show text…they’re not actually going to do anything. They just have to look pretty. All I did was create the first one, copy it 5 times, and drag each one to their positions. You should notice that Blend provides some nice guides for alignment purposes. It makes life easy. Here’s how I have laid them out:

And here’s the XAML document in its entirety, thus far:

<UserControl x:Class="NavigationSample.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="200">
<
Canvas x:Name="LayoutRoot" Background="White">
<
Rectangle Width="780" Height="100" Stroke="#FF666666" RadiusY="15" RadiusX="15" StrokeThickness="2" Canvas.Top="50" Canvas.Left="11">
<
Rectangle.Fill>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="#FFC4C4C4" Offset="1"/>
<
GradientStop Color="#FFFFFFFF" Offset="0"/>
</
LinearGradientBrush>
</
Rectangle.Fill>
</
Rectangle>
<
Rectangle Height="80" Width="760" RadiusY="7" RadiusX="7" StrokeThickness="2" Stroke="#FFB1B1B1" Canvas.Left="21" Canvas.Top="60">
<
Rectangle.Fill>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="#FFF7D599" Offset="1"/>
<
GradientStop Color="#FFE8AF34"/>
</
LinearGradientBrush>
</
Rectangle.Fill>
</
Rectangle>
<
TextBlock Text="Home" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="49" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="Archive" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="168" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="About" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="312" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="Code" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="443" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="Slides" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="570" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="Contact" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="684" Foreground="#FF6D6D6D" FontWeight="Normal"/>
</
Canvas>
</
UserControl>

8. We need to make the buttons change color.

Before, I said that the TextBlocks don’t actually do anything, and that’s true. What I am going to do instead is cover each of those TextBlocks with a semi-transparent Rectangle, and that’s the XAML element that is going to do all of the work. I’m also going to add the vertical gray seperators between each TextBlock, but those are just Rectangles. Nothing very exciting. You’ll see it in the XAML. Here’s my rectangles:

 <Rectangle Height="76" Width="109" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="23" Canvas.Top="62" x:Name="Home"/>
<
Rectangle Height="76" Width="139" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="133" Canvas.Top="62" x:Name="Archive"/>
<
Rectangle Height="76" Width="132" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="273" Canvas.Top="62" x:Name="About"/>
<
Rectangle Height="76" Width="126" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="406" Canvas.Top="62" x:Name="Code"/>
<
Rectangle Height="76" Width="122" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="533" Canvas.Top="62" x:Name="Slides"/>
<
Rectangle Height="76" Width="122" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="656" Canvas.Top="62" x:Name="Contact"/>

There are two things special about these rectangles. The first is that I have actually named them. Each one has an x:Name property, with an indication of which TextBlock it is covering. The second thing you should notice is that they have an opacity set to ZERO. This means that they are completely transparent. An opacity of 1 means that you can’t see through them at all. This is the property we are going to manipulate with some code. Here’s what the nav looks like now, and the XAML will follow it.

<UserControl x:Class="NavigationSample.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="200">
<
Canvas x:Name="LayoutRoot" Background="White">
<
Rectangle Width="780" Height="100" Stroke="#FF666666" RadiusY="15" RadiusX="15" StrokeThickness="2" Canvas.Top="50" Canvas.Left="11">
<
Rectangle.Fill>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="#FFC4C4C4" Offset="1"/>
<
GradientStop Color="#FFFFFFFF" Offset="0"/>
</
LinearGradientBrush>
</
Rectangle.Fill>
</
Rectangle>
<
Rectangle Height="80" Width="760" RadiusY="7" RadiusX="7" StrokeThickness="2" Stroke="#FFB1B1B1" Canvas.Left="21" Canvas.Top="60">
<
Rectangle.Fill>
<
LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<
GradientStop Color="#FFF7D599" Offset="1"/>
<
GradientStop Color="#FFE8AF34"/>
</
LinearGradientBrush>
</
Rectangle.Fill>
</
Rectangle>
<
TextBlock Text="Home" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="49" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="Archive" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="168" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="About" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="312" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="Code" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="443" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="Slides" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="570" Foreground="#FF6D6D6D" FontWeight="Normal"/>
<
TextBlock Text="Contact" FontFamily="ROCK.TTF#Rockwell" FontSize="20" Canvas.Top="91" Canvas.Left="684" Foreground="#FF6D6D6D" FontWeight="Normal"/>

<
Rectangle Height="77" Width="1" Fill="#FFB1B1B1" Stroke="{x:Null}" Canvas.Top="62" Canvas.Left="132" x:Name="Divider1"/>
<
Rectangle Height="77" Width="1" Fill="#FFB1B1B1" Stroke="{x:Null}" Canvas.Top="62" Canvas.Left="272" x:Name="Divider2"/>
<
Rectangle Height="77" Width="1" Fill="#FFB1B1B1" Stroke="{x:Null}" Canvas.Top="62" Canvas.Left="405" x:Name="Divider3"/>
<
Rectangle Height="77" Width="1" Fill="#FFB1B1B1" Stroke="{x:Null}" Canvas.Top="62" Canvas.Left="532" x:Name="Divider4"/>
<
Rectangle Height="77" Width="1" Fill="#FFB1B1B1" Stroke="{x:Null}" Canvas.Top="62" Canvas.Left="655" x:Name="Divider5"/>
<
Rectangle Height="76" Width="109" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="23" Canvas.Top="62" x:Name="Home" MouseEnter="MouseOver" MouseLeave="MouseOut" MouseLeftButtonDown="MouseClick" />
<
Rectangle Height="76" Width="139" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="133" Canvas.Top="62" x:Name="Archive" MouseEnter="MouseOver" MouseLeave="MouseOut" MouseLeftButtonDown="MouseClick" />
<
Rectangle Height="76" Width="132" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="273" Canvas.Top="62" x:Name="About" MouseEnter="MouseOver" MouseLeave="MouseOut" MouseLeftButtonDown="MouseClick" />
<
Rectangle Height="76" Width="126" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="406" Canvas.Top="62" x:Name="Code" MouseEnter="MouseOver" MouseLeave="MouseOut" MouseLeftButtonDown="MouseClick" />
<
Rectangle Height="76" Width="122" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="533" Canvas.Top="62" x:Name="Slides" MouseEnter="MouseOver" MouseLeave="MouseOut" MouseLeftButtonDown="MouseClick" />
<
Rectangle Height="76" Width="122" Opacity="0" Fill="#FFFFFFFF" Stroke="{x:Null}" Canvas.Left="656" Canvas.Top="62" x:Name="Contact" MouseEnter="MouseOver" MouseLeave="MouseOut" MouseLeftButtonDown="MouseClick" />
</
Canvas>
</
UserControl>

9. We need some event handlers.

Each of the Rectangles that we just created need event handlers so that we can do things when the user rolls over them, as well as clicks on them. All we need to add to each of those 6 Rectangles is this code:

MouseEnter="MouseOver" MouseLeave="MouseOut" MouseLeftButtonDown="MouseClick"

I actually already added them for you in the XAML from step #8, but I want to make sure you know why they are there. We’re going to define the methods in our next step.

10. Let’s write some event handler methods!

So our XAML is technically done. If our designer wanted to change stuff later, he/she can do whatever he/she wants. We just need to write some methods in the Page.xaml code-behind file, and we’re done with this little project.

Before we do that, however, I’ve got one small task for you. In order to do this efficiently, we’re going to need to add a reference to this Silverlight project. We’re adding System.Net. To add a reference, just right click on the “References” folder in your project, and choose “Add Reference…” In the box that appears, just choose System.Net, and click “OK.”

The reason we’re doing this is so that we have access to the HtmlWindow and HtmlPage classes. We’ll be using those in a method to redirect our user to another page.

Our MouseOver and MouseOut methods are very similar. One is going to add some opacity to our Rectangles, and the other will remove it again. This will give us a very nice rollover effect when the user’s mouse moves over each nav element. Here’s what those methods look like:

 private void MouseOver(object sender, MouseEventArgs e)
{
Rectangle activebox = sender as Rectangle;
activebox.Opacity = .2;
}

private void MouseOut(object sender, MouseEventArgs e)
{
Rectangle activebox = sender as Rectangle;
activebox.Opacity = 0;
}

Our (kinda) final method is the one to handle clicking on the nav. Ideally, when a user clicks on a nav element, they expect to be whisked away to a new page. And that’s the experience we are going to provide them. Here’s the method:

 private void MouseClick(object sender, MouseButtonEventArgs e)
{
Rectangle activebox = sender as Rectangle;
if (activebox != null)
{
switch (activebox.Name.ToString().ToUpper())
{
case "HOME":
LaunchNewPage("http://jeffblankenburg.com/default.aspx");
break;
case "ARCHIVE":
LaunchNewPage("http://jeffblankenburg.com/archive.aspx");
break;
case "ABOUT":
LaunchNewPage("http://jeffblankenburg.com/aboutjeffblankenburg.aspx");
break;
case "CODE":
LaunchNewPage("http://jeffblankenburg.com/code.aspx");
break;
case "SLIDES":
LaunchNewPage("http://jeffblankenburg.com/slides.aspx");
break;
case "CONTACT":
LaunchNewPage("http://jeffblankenburg.com/contact.aspx");
break;
}
}
}

private void LaunchNewPage(string URI)
{
{
HtmlWindow window = HtmlPage.Window;
Uri uri = new Uri(URI);
window.Navigate(uri);
}
}

You’ll see that the cases in the switch statement should exactly match the names of the 6 Rectangles that we are using, but capitalized. Since I’m never certain exactly which way I capitalized my names, I find it easier just to compare those names converted to all capital letters. That’s what the .ToUpper() is for in the switch statement.

You should also see that we call a method named “LaunchNewPage()” in each case. I have extracted the page redirect into its own function. The reason for this, at least initially, was because I wasn’t sure if I wanted to open new windows for each click, or if I just wanted to redirect the user in the same browser window. By extracting this functionality, I can easily change it later for all of them, without having to touch each individual case. I do this for things like alert() in javascript as well. That way, if later, I want to make those alert boxes something nicer, like a popup DIV or even another window, I can do it easily.

So there you go! We’ve got a working navigation!

Click here to see the Silverlight Navigation working…

Click here to get the source code for this entire solution…

kick it on DotNetKicks.com

5 thoughts on “TUTORIAL #11: Creating a Navigation In Silverlight

  1. questionm did u had to do anything special embed ur silverlight menu? or simply embed it?isnt it risky since normal people dont have silverlight? what do you think?

  2. I think it is very good post.I am a developer on ASP.NET. But I am really weak in design such a cool stuff. Can you advice me on how should I proceed. I just don’t have any idea how to proceed when I open expression blend.I know this is an off topic discussion, but still.

  3. Great post Jeff – I’m considering something similar for my planned “blog facelift” sometime this spring.Quick question – is there any advantage to using the Rectangles with the code-behind events rather that templated buttons with visual styles. The Rectangle example has less XAML and more C# (which could be an advantage in itself for people new to Silverlight), but I’d think a button with styles and visual states could give more options later (and only need the Click event in code).Just wondering – have a great weekend.

  4. Great tutorial, Jeff. Learned alot from it. Thanks. :)However, a little correction: – Namespace required for 'HtmlWindow' and 'HtmlPage' classes is "System.Windows.Browser" namespace.Please correct me if I am wrong.Regards,Syed

  5. Use a Setter Property for the Cursor to be “Hand” and you’ll really have a nice navigation going =)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s