Jeff Blankenburg is a passionate technologist with a wide range of interests.
This website is dedicated to discussing the ideas that pass through his head.

The main header for this website was created using Microsoft Silverlight.
Install it to see what you're missing!

Get Silverlight to see the whole site!

Day #4: Communicating Between Two Silverlight Controls

Saturday, July 04, 2009

Have you ever wondered how to get more than one Silverlight element on your page? Have you ever wondered how to get two Silverlight controls to communicate? This post will address both of those questions.

For those of you not pondering those questions, here's a common scenario: You want to have a common Silverlight navigation on your page, and you don't want to redirect the user to another page for each click. Instead, we want a second Silverlight element on the page that changes as we click the nav.

It's generally bad user experience to put everything in one, giant Silverlight container, because it will take FOREVER to load, forcing your users to sit and wait before they can do anything. By being more modular, we improve load times, and it's actually pretty simple to do. Let's get started:

1. Create your Silverlight project.




Make sure you create your accompanying Web project as well..



2. Make this XAML unique.

I'm just going to make the background of the Silverlight app orange. I've also added a "1" so that I'm sure which control it is. When we put 2 Silverlight controls on a page, we want to make sure we know which is which. There are also three buttons on this screen. When the user clicks those buttons, we're going to change the background color of the other Silverlight control. We'll get to the event handler code a little later. (You might also need to add the System.Windows.Browser namespace...it's for talking to the DOM). Here's the XAML I am using:

<UserControl x:Class="SilverlightWithTwoControls.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<
StackPanel x:Name="LayoutRoot" Background="Orange" HorizontalAlignment="Center" VerticalAlignment="Center" Width="400" Height="300">
<
TextBlock Text="1" FontSize="100"></TextBlock>
<
Button x:Name="Green" Content="Green" Width="100" Height="25" Margin="10" Click="Button_Click" />
<
Button x:Name="Purple" Content="Purple" Width="100" Height="25" Margin="10" Click="Button_Click" />
<
Button x:Name="Blue" Content="Blue" Width="100" Height="25" Margin="10" Click="Button_Click" />
</
StackPanel>
</
UserControl>


3. Create another Silverlight project.

Each Silverlight project will be independent of each other. Each Silverlight project creates ONE XAP file, and since we need two, this is the best solution.



4. Mark up our second XAML file.

This file is even simpler. We're only modifying the background color and adding a 2 for identification. Here's the code:

<UserControl x:Class="SecondControl.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<
Canvas x:Name="LayoutRoot" Background="#666666" HorizontalAlignment="Center" VerticalAlignment="Center" Width="400" Height="300">
<
TextBlock Text="2" FontSize="100"></TextBlock>
</
Canvas>
</
UserControl>


5. Let's add these to our .aspx page.

We're just going to have 2 <asp:Silverlight> tags on our page in this example, pointing to the two individual .xap files. Here's what the HTML looks like:

<%@ Page Language="C#" AutoEventWireup="true" %>
<%
@ Register Assembly="System.Web.Silverlight" Namespace="System.Web.UI.SilverlightControls" TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<
html xmlns="http://www.w3.org/1999/xhtml" style="height:100%;">
<
head runat="server">
<
title>Silverlight With Two Controls</title>
</
head>
<
body style="height:100%;margin:0;">
<
form id="form1" runat="server" style="height:100%;">
<
asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<
div style="position:absolute;top:10px;left:10px;">
<
asp:Silverlight ID="Xaml1" runat="server" Source="/ClientBin/SilverlightWithTwoControls.xap" MinimumVersion="2.0.31005.0" Width="400" Height="300" />
</
div>
<
div style="position:absolute;top:320px;left:10px;">
<
asp:Silverlight ID="Xaml2" runat="server" Source="/ClientBin/SecondControl.xap" MinimumVersion="2.0.31005.0" Width="400" Height="300" />
</
div>
</
form>
</
body>
</
html>


6. Writing the button event handler.

We need those buttons to actually do something. Let's write that event handler code. Here's what it looks like:

private void Button_Click(object sender, RoutedEventArgs e)
{
Button clicky = sender as Button;
HtmlPage.Window.Invoke("changeColor", clicky.Name);
}


7. Say it ain't so...

I don't know how many times I can tell you that Javascript rules the world, but here's another example. We're going to leverage the power of Javascript to talk between our Silverlight objects. Because it has the ability to leverage the Silverlight DOM, it's actually a very useful and powerful tool. (And for all of you naysayers, if a user has Javascript turned off, it's HIGHLY likely they're not running rich UI plugins like Silverlight or Flash either.)

Anyways, there's a pretty simple Javascript function we can write to get this action taken care of. In our previous step, we specified the name of the Javascript function, changeColors. We also pass the Name value of the Button, which just happens to be the name of the color we're going to be changing the other control to. Forethought is cool. Here's what my Javascript function looks like:

function changeColor(color) {
slObject = document.getElementById("Xaml2");
slObject.Content.Page.ChangeBackgroundColor(color);
}


What we've done here is call the method ChangeBackgroundColor inside the Xaml2 control. We're passing the color we want to change it to.

8. Writing the final method in the 2nd control.

Now we just need to write that method in the code-behind of our second XAML file, and we should be done. Here's my final code for the second control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;

namespace SecondControl
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}

[ScriptableMember]
public void ChangeBackgroundColor(string colorName)
{
if (colorName == "Blue")
{
LayoutRoot.Background = new SolidColorBrush(Colors.Blue);
}
else if (colorName == "Purple")
{
LayoutRoot.Background = new SolidColorBrush(Colors.Purple);
}
else if (colorName == "Green")
{
LayoutRoot.Background = new SolidColorBrush(Colors.Green);
}
}
}
}


You might have noticed that there's something a little different in there. We have a decorator [ScriptableMember] in there. We had to make some changes to our application to make it exposed to Javascript. By default, it can't just call inside our Silverlight app. We have to make that happen in code.

9. Editing our App.xaml file.

In the second Silverlight project, open your app.xaml file. In our Application_Startup method, you should have a line that looks like
this.RootVisual = new Page();
. We're going to add one more line after that, which registers our control as a Scriptable object. Here's the entire method, after my modifications:

private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Page();
HtmlPage.RegisterScriptableObject("Page", this.RootVisual);
}


This line of code, in conjunction with the decorator we added in Step #8 will mean we're done, and that our controls can now affect each other.

Sample Code

You can download the sample code for this example here. You should also be able to see these controls below this paragraph, but RSS readers like to ignore it. If it's not showing up, you can also see Two Silverlight Controls together by clicking here.










Get Microsoft Silverlight












Get Microsoft Silverlight

Labels: , ,

posted by Jeff Blankenburg, 1:00 AM | link | 0 comments |

Day #3: Custom Silverlight Loading Screen

Friday, July 03, 2009

This is the third post in my series of thirty-one, called, not surprisingly, "31 Days of Silverlight". You can find the master list here.

Also, before we get started here, don't forget that today is Contribupendence Day. Take a moment to read this post about what Contribupedence Day is, and how you can participate.

If you've ever seen a Silverlight application before, you've likely seen the default loading screen. Here's a example of what I'm talking about:



Now, there's nothing wrong with it, except that it's the thing you see EVERYWHERE. There's a way to customize this screen, and you should. It's an opportunity to re-inforce your branding, entertain your user, and differentiate yourself from everyone else. Let's get started.

Building Our Loading Screen

The first thing to know about this loading screen is that it functions completely outside of our .xap file. If you think about it though, it makes sense. We're trying to monitor the loading percentage of our XAP file, so we could hardly expect our loading code to be inside it, right?

So, instead of creating a new XAML file inside of our Silverlight project, we're going to create a XAML file inside our Web Project this time. When you choose "Add > New Item...", you're going to choose the "Silverlight JScript File." This will create a XAML file in our web project, but otherwise, it's your standard XAML. I've created one that has a TextBlock to display the percentage as a number, as well as a rectangle underneath. This rectangle will be my percentage progress bar. Here's the code (I named it LoadingScreen.xaml):

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300">
<
Canvas x:Name="LayoutRoot" Background="#FFB5EFFF">
<
Rectangle Height="4" Width="0" Fill="#FFE8B03A" Stroke="#FF999999" StrokeThickness="1" x:Name="ProgressBar" Canvas.Left="14" Canvas.Top="156"/>
<
TextBlock Width="100" FontFamily="ROCK.TTF#Rockwell" FontSize="48" Text="Blankenthoughts" Foreground="#FF6D6D6D" x:Name="Title" Canvas.Top="56" Canvas.Left="150" />
</
Canvas>
</
Canvas>


Getting Our Loading Screen Loaded

So we've got a loading screen now, but we also need to make sure that our Silverlight <object> tag knows that we've got a custom loading screen. We do this with a param tag in the HTML. The splashscreensource tag specifies a XAML file that should be used instead of the default. Here's how my entire HTML file looks now:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<
html xmlns="http://www.w3.org/1999/xhtml" >
<
head>
<
title>SilverlightLoadingScreen</title>
</
head>

<
body>
<
div id="silverlightControlHost">
<
object id="xamlobject" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<
param name="splashscreensource" value="LoadingScreen.xaml"/>
<
param name="source" value="ClientBin/SilverlightLoadingScreen.xap"/>
<
param name="background" value="white" />
</
object>
</
div>
</
body>
</
html>


What you may notice, if you're building along with me, is that the loading screen loads, but it doesn't change as the load progresses. This is because we need to pay attention to the "onSourceDownloadProgressChanged" event. Thankfully, we do with with another param tag in our <object> tag. The value that we provide is actually the name of the Javascript function we will write. That tag looks like this:

<param name="onSourceDownloadProgressChanged" value="onSourceDownloadProgressChanged" />


You didn't tell me there'd be Javascript

Ah, never doubt for a second that the Internet runs on Javascript. Of course it does! This function is simple, and is the glue that puts our XAML together with actual data. I'll give you my function first, and then we can discuss the different parts of it.

function onSourceDownloadProgressChanged(sender, eventArgs)
{
var slObject = document.getElementById("xamlobject");
var progressBar = slObject.content.findName("ProgressBar");
var progressTitle = slObject.content.findName("Title");
if (eventArgs.progress)
{
progressBar.Width = eventArgs.progress * 370;
progressTitle.Text = parseInt(eventArgs.progress * 100) + "%";
}
else
{
progressBar.Width = eventArgs.get_progress() * 370;
progressTitle.Text = parseInt(eventArgs.get_progress() * 100) + "%";
}
}


  • slObject - this is a reference to my Silverlight <object> tag on the page. I need to get this in order to find the child elements, specifically my Rectangle and TextBlock elements.
  • progressBar - this is a reference to my ProgressBar Rectangle element within my XAML document. Don't you wish getting through XML was always that easy? It just searches through the document tree until it finds an element with the name I specified.
  • progressTitle - a TextBlock in the XAML that will hold the actual percentage of completion.
  • eventArgs.progress - this is the physical number that represents our completion percentage. We use this to modify the width of our rectangle, so that it grows as the percentage increases. I am also displaying this number in the TextBlock, appending a "%" to the end.
  • eventArgs.get_progress()
  • - basically, we need to call the get_progress method in case our page is not already paying attention to it. Once this has been called, however, it will default to the previous part of the if statement, using just the progress property of the eventArgs.


Can I See A Working Example?

Absolutely. Below is a working example of this custom loading screen in action. If you don't see it, you're probably reading this post from an RSS reader. Click here to see the Silverlight Loading Screen as a working example. You can also click here to download my entire set of working code.












Get Microsoft Silverlight

Labels: , ,

posted by Jeff Blankenburg, 1:00 AM | link | 2 comments |

Day #2: Silverlight Screen Transitions

Thursday, July 02, 2009

This is second of thirty-one posts on Silverlight I will be doing in July. The first post was on the mouse events available to you in Silverlight. Today's post is on Screen Transitions, or more specifically, moving from one XAML file to another.

Moving From One XAML File To Another

This is the simple part. In your Silverlight project (mine is called SilverlightScreenTransitions), add a new XAML file. You can do this by right clicking on the project name, and choosing "Add > New Item." You won't see "XAML File" as one of the choices, because each XAML file in Silverlight is considered a User Control. So choose "Silverlight User Control," and give it a meaningful name, like page2.xaml. :)

Now jump back into your default XAML file, Page.xaml. We need a button, or a link, or something that will allow us to navigation to our new page. Here's the new XAML for our default page:

<UserControl x:Class="SilverlightScreenTransitions.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<
Canvas x:Name="LayoutRoot" Background="White">
<
Rectangle x:Name="Clicky" Width="200" Height="50" Fill="Red" Canvas.Top="125" Canvas.Left="100" Stroke="Black" StrokeThickness="4" MouseLeftButtonUp="Clicky_MouseLeftButtonUp"/>
</
Canvas>
</
UserControl>


All this does is put a red rectangle in the middle of our control. Next, we're going to need to edit our second page. We'll just put a "Congratulations!" textbox on the page, so we know we made it. Here's the XAML for Page2.xaml:

<UserControl x:Class="SilverlightScreenTransitions.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<
Canvas x:Name="LayoutRoot" Background="White">
<
TextBlock Width="200" Height="50" Canvas.Top="125" Canvas.Left="100" Text="Congratulations!" TextAlignment="Center" FontSize="25"/>
</
Canvas>
</
UserControl>


The magic line of code

At this point, we have two XAML files, one with a rectangle (with an event handler), and the other with just a TextBlock. When we click on the Rectangle, we want to go to the other XAML file. Here's how you do it (in VB.NET, just ignore the semi-colon). Just add this line to your event handler code in Page.xaml.cs (or .vb):

this.Content = new Page2();


I thought this post was about transitions...

Oh, patience, my friends. Now that we know how to get from one screen to another, we can talk about transitions. Let me first start by defining transition:

The animated movement from one XAML file to another. Think transitions in Powerpoint. Fades, Dissolves, etc. We'll create an example of that in this post.

Fading In and Out

We're going to create a fading transition from the first slide to the second. What I'm going to do is create an animation in each XAML file. In Page.xaml, we'll create an animation that fades out. In Page2.xaml, we'll create an animation that fades it in. Between the two, we'll jump from one file to the next, using the code we wrote earlier.

Page.xaml animation

The only difference is the new Storyboard I created. It just takes the opacity of the LayoutRoot element from 100% to 0% in one second:

<UserControl x:Class="SilverlightScreenTransitions.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<
UserControl.Resources>
<
Storyboard x:Name="FadeOut">
<
DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Opacity)">
<
SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
<
SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
</
DoubleAnimationUsingKeyFrames>
</
Storyboard>
</
UserControl.Resources>
<
Canvas x:Name="LayoutRoot" Background="White" Opacity="1">
<
Rectangle x:Name="Clicky" Width="200" Height="50" Fill="Red" Canvas.Top="125" Canvas.Left="100" Stroke="Black" StrokeThickness="4" MouseLeftButtonUp="Clicky_MouseLeftButtonUp"/>
</
Canvas>
</
UserControl>


Page2.xaml animation

We are doing the exact opposite animation for this file, but there's one other thing to consider in this case. Even though our animation takes the opacity from 0% to 100%, we need to make sure that the initial opacity of our LayoutRoot element is set to 0%. Without that, the XAML file will load with it at 100%, and then the animation will kick off, fading it from 0 to 100. Not exactly the desired effect. So you'll see the opacity specified in the LayoutRoot element as well.

<UserControl x:Class="SilverlightScreenTransitions.Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<
UserControl.Resources>
<
Storyboard x:Name="FadeIn">
<
DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="(UIElement.Opacity)">
<
SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<
SplineDoubleKeyFrame KeyTime="00:00:01" Value="1"/>
</
DoubleAnimationUsingKeyFrames>
</
Storyboard>
</
UserControl.Resources>
<
Canvas x:Name="LayoutRoot" Background="White" Opacity="0">
<
TextBlock Width="200" Height="50" Canvas.Top="125" Canvas.Left="100" Text="Congratulations!" TextAlignment="Center" FontSize="25"/>
</
Canvas>
</
UserControl>


An Animation Event Handler?

So we obviously also need to change our code-behind, so that when we click, we call the animation. But how do we know when the animation is done? Well, we know it's going to take exactly ONE second, so technically, we could create a timer, wait 1 second, and then make the call to the second XAML file. But that's messy, and depending on ANY sort of lag, would be inaccurate. Thankfully, we have the ability to add event handlers to our animations as well. So, we can create a "Completed" event handler on our FadeOut animation, which will get called immediately after the animation completes. No guess work. Here's our new code-behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightScreenTransitions
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
FadeOut.Completed += new EventHandler(FadeOut_Completed);
}

void FadeOut_Completed(object sender, EventArgs e)
{
this.Content = new Page2();
}

private void Clicky_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
FadeOut.Begin();
}
}
}


Catching the animation on the other side

Transitioning into the next XAML file is simple, in the initial method, we call the FadeOut animation we created.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightScreenTransitions
{
public partial class Page2 : UserControl
{
public Page2()
{
InitializeComponent();
FadeIn.Begin();
}
}
}


The final working example

Below is the Silverlight application, up and running. If you're viewing this in an RSS reader, it may not show up, but you can click here to see the Silverlight Screen Transition working. You can also download all of the source code for this project here.







Get Microsoft Silverlight

Labels: , ,

posted by Jeff Blankenburg, 1:00 AM | link | 2 comments |

Search

My Sponsor


My Badges



Follow Jeff Blankenburg on Twitter