Silverlight class library not included in .XAP

July 10, 2011 15:51

This is something I'm sure I ran into before, but it still bit me the other day. Hopefully by writing this down here it won't get me again.

The problem is this: A Silverlight class library whose name ends with ".Resources" is not included in the Silverlight .XAP file.

".Resources" is a reserved naming convention (I guess) that shouldn't be used. The solution to including such DLL's in the .XAP file is to rename it so it ends with something like ".SharedResources.dll".

Short post. Odd problem. Easy solution.

[ Follow me on Twitter ]


A basic Silverlight project using the MVVM design pattern

May 20, 2011 08:07

I've been immersing myself in the Model-View-ViewModel (MVVM) design pattern of late. The pattern derives from Model-View-Presenter (MVP), which in turn has its roots in the Model-View-Controller (MVC) pattern. MVVM takes MVP one step further by gearing itself specifically towards WPF/Silverlight style development. While both WPF and Silverlight have built-in support for implementing MVVM (commanding, bindings, behaviors via the Expression Blend SDK), they do so with varying depth, and, even still, both fall short in terms of providing comprehensive support. Fortunately, a wide array of MVVM frameworks have cropped up to fill in these holes.

In this post I wanted to go through the motions of setting up a skeleton Silverlight project that utilizes MVVM. The application doesn't do much (it displays a random number in response to user input), but it does demonstrate some of the basic facilities we can use to implement the MVVM pattern, namely commanding and binding. One of the primary goals of MVVM is to remove the (often tight) coupling between the presentation layer (XAML) and the business logic (code-behind). Some argue that the pattern is overrated; we're simply removing logic from the code-behind and putting it into the ViewModel. This is true to some extent. But if you create a true separation between your UI and logic, and you take full advantage of the platform's support for MVVM, ultimately you'll find yourself writing less code. That's a good thing.

Much of what I show below is encapsulated in the various MVVM frameworks. I'm ignoring those for now because I want to establish a deeper understanding of the implementation mechanics. Moving forward, I will most certainly be adopting one of the frameworks.

Now, on to the code…

The Silverlight MVVM Project

1. Create a new Silverlight project

Standard stuff. Create a new Silverlight project, giving it a host web site. I called mine "MVVMBase".

2. Add three new folders to the Silverlight project

imageThe three folders are:

  1. MVVMFramework
  2. ViewModels
  3. Views

3. In the MVVMFramework folder, add support classes

These are the classes that any of the MVVM frameworks will supply for you. Even if you choose to not use one of the frameworks and roll your own, you'll likely want to place these objects in their own reusable class library since any MVVM project will make use of them. As noted above, I'm rolling my own here simply as a learning exercise.

Here are the classes. There are three of them.

a. Observable

When we define our own ViewModel classes, it is necessary that those objects derive from INotifyPropertyChanged because we'll be using the class in binding operations. Normally, you might create a ViewModelBase class, derive it from INotifyPropertyChanged, and you're done.  It's been suggested by some, however, to separate out INotifyPropertyChanged functionality from the base ViewModel class since there are instances other than when defining a ViewModel where you might want to derive from INotifyPropertyChanged. That's what I've done here. Observable is defined as:

   1: namespace MVVMBase.MVVMFramework
   2: {
   3:     using System.ComponentModel;
   4:     using System.Windows;
   5:  
   6:     public class Observable : INotifyPropertyChanged
   7:     {
   8:         public event PropertyChangedEventHandler PropertyChanged;
   9:  
  10:         protected void NotifyPropertyChanged (string propertyName)
  11:         {
  12:             if (PropertyChanged != null)
  13:             {
  14:                 PropertyChanged (this, new PropertyChangedEventArgs (propertyName));
  15:             }
  16:         }
  17:     }
  18: }

b. ViewModelBase

It's convenient to have a ViewModel base class from which your own ViewModel classes can derive. For purposes of this post, mine doesn't do much except derive from Observable:

   1: namespace MVVMBase.MVVMFramework
   2: {
   3:     public class ViewModelBase : Observable
   4:     {
   5:     }
   6: }

c. RelayCommand

WPF introduced something called Commanding, which allows you to bind a user event (like a button click) to execution logic where the binding is done in XAML. User-defined commands are derived from ICommand; you need to have one defined per command event. Of course, if you define a new ICommand for each and every command, you'll quickly have a lot of repetitive code. Fortunately, there's a better way.

RelayCommand (alternatively named DelegateCommand or ViewModelCommand by others) is a generic class that derives from ICommand. It takes as parameters an Action and a Predicate, which define ICommand's Execute and CanExecute methods which RelayCommand must implement.

Here's the code:

   1: namespace MVVMBase.MVVMFramework
   2: {
   3:     using System;
   4:     using System.Windows.Input;
   5:  
   6:     public class RelayCommand : ICommand
   7:     {
   8:         private readonly Action<object> _executeAction;
   9:  
  10:         private readonly Predicate<object> _canExecute;
  11:  
  12:         public RelayCommand (Action<object> executeAction, Predicate<object> canExecute)
  13:         {
  14:             if (executeAction == null)
  15:             {
  16:                 throw new ArgumentNullException ("executeAction");
  17:             }
  18:  
  19:             _executeAction = executeAction;
  20:             _canExecute = canExecute;
  21:         }
  22:  
  23:         public event EventHandler CanExecuteChanged;
  24:  
  25:         public void OnCanExecuteChanged ()
  26:         {
  27:             if (CanExecuteChanged != null)
  28:             {
  29:                 CanExecuteChanged (this, EventArgs.Empty);
  30:             }
  31:         }
  32:  
  33:         /// <summary>
  34:         /// Defines the method that determines whether the command can execute in its current state.
  35:         /// </summary>
  36:         /// <returns>True if this command can be executed; otherwise, false.</returns>
  37:         /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
  38:         public bool CanExecute (object parameter)
  39:         {
  40:             return _canExecute == null || _canExecute (parameter);
  41:         }
  42:  
  43:         /// <summary>
  44:         /// Defines the method to be called when the command is invoked.
  45:         /// </summary>
  46:         /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
  47:         public void Execute (object parameter)
  48:         {
  49:             _executeAction (parameter);
  50:         }
  51:     }
  52: }

4. In the ViewModels folder, add a new class called "MainPageViewModel"

This is our ViewModel, derived from ViewModelBase, which contains the business logic for updating our View. Note that the ViewModel doesn't update the View directly. Instead, it updates properties that have been bound to the View in the View's XAML.

   1: namespace MVVMBase.ViewModels
   2: {
   3:     using MVVMBase.MVVMFramework;
   4:  
   5:     using RandomNumber;
   6:  
   7:     public class MainPageViewModel : ViewModelBase
   8:     {
   9:         private IGenerateNumber _generateNumber;
  10:  
  11:         private string _number;
  12:  
  13:         public MainPageViewModel (IGenerateNumber generateNumber)
  14:         {
  15:             _generateNumber = generateNumber;
  16:  
  17:             GetNumberCommand = new RelayCommand (GetNumber, CanGetNumber);
  18:         }
  19:  
  20:         public RelayCommand GetNumberCommand { get; private set; }
  21:  
  22:         public string Number
  23:         {
  24:             get
  25:             {
  26:                 return _number;
  27:             }
  28:  
  29:             private set
  30:             {
  31:                 if (value == _number)
  32:                     return;
  33:  
  34:                 _number = value;
  35:  
  36:                 NotifyPropertyChanged ("Number");
  37:             }
  38:         }
  39:  
  40:         private void GetNumber (object parameter)
  41:         {
  42:             Number = _generateNumber.GetRandomNumber ().ToString ();
  43:         }
  44:  
  45:         private bool CanGetNumber (object parameter)
  46:         {
  47:             return true;
  48:         }
  49:     }
  50: }

The constructor takes an interface to GenerateNumber, which is just a class library whose code is included in the download (see below). This service class generates a random number and returns it to the caller via the GetRandomNumber method.

GetNumber and CanGetNumber correspond to the RelayCommand's Action and Predicate delegates. ICommand gives us the ability to turn off a feature via a CanExecute method and to execute some logic in response to a command via a Execute method. That's the role these two delegates play. In CanGetNumber I simply return 'true'; in a real application you might have logic here that actually checks conditions and then possibly returns false if you do not want the command to execute. GetNumber runs if and when CanExecute evaluates to 'true'. Here it initializes the POCO property Number with a new random number. See the discussion beneath the View code for what's going on behind the scenes.

5. In the Views folder, copy over and rename MainPage.xaml/.cs

MainPage.xaml/.cs was created when you created the Silverlight project. Rename it to MainPageView.xaml/.cs and copy it into the Views folder (you don't have to rename it, but I do just for naming consistency; remember to rename the class, too).

First, let's look at the codebehind:

   1: namespace MVVMBase.Views
   2: {
   3:     using System.Windows.Controls;
   4:  
   5:     public partial class MainPageView : UserControl
   6:     {
   7:         public MainPageView ()
   8:         {
   9:             InitializeComponent ();
  10:         }
  11:     }
  12: }

As we intended, it pretty much contains nothing. This is as it should be when implementing the MVVM pattern, though note that achieving this ideal while easy in a small project like this one, can be very difficult and almost counter-productive in a real-world, complex application.

Next, take a look at the MainPageView.xaml file:

   1: <UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="MVVMBase.Views.MainPageView"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:     mc:Ignorable="d"
   7:     d:DesignHeight="200" d:DesignWidth="150">
   8:  
   9:     <StackPanel x:Name="LayoutRoot" Orientation="Vertical" Background="White" VerticalAlignment="Center">
  10:         <sdk:Label Content="{Binding Number}" Height="30" Width="100" Background="AliceBlue" Margin="10" />
  11:         <Button Content="Get Number" Height="30" Width="100" Margin="10" Command="{Binding GetNumberCommand}" />
  12:     </StackPanel>
  13: </UserControl>

The important stuff is on lines 10-11.

On Line 10, we define a Label whose Content is bound to a property called "Number". If you go back and look at the ViewModel, you'll see I defined a POCO property there that corresponds to this binding. The magic is the call to NotifyPropertyChanged, which notifies the binding framework that this property's value has changed. This in turn triggers an update to our UI because of the Binding we set up.

On Line 11, we declare a Button whose Command property is bound to something called "GetNumberCommand". Again, looking back at MainPageViewModel, you'll see a RelayCommand that corresponds to this binding.

6. Connect the View to the ViewModel

At this point we have all the infrastructure in place for a basic Silverlight MVVM application. But we still need to connect the View to the ViewModel. There are several philosophies out there on how best to do this. The basic options are these (Pete Brown touches on these approaches in his book, Silverlight 4 in Action):

1.) The code owns the ViewModel

In this approach you instantiate the ViewModel in the View's code-behind and assign it to the View's DataContext. While straightforward, this is not a recommended approach mostly because you've created a situation where the View cannot be used without also pulling the ViewModel along with it.

2.) The markup owns the ViewModel

This is essentially the same thing as the previous approach except in this case you declare the ViewModel in the View's XAML. Again, while relatively straightforward, you're creating a tightly bound relationship between the View and the ViewModel. This isn't necessarily a bad thing; in MVVM, a View is going to have a ViewModel, right? The truth of the matter is that either this approach or the previous one works perfectly fine. But option #3 has the added benefit of additional flexibility.

3.) The ViewModel is provided externally

In this approach the ViewModel is instantiated externally from the View and the two are bound together outside of either the View or the ViewModel. The benefit of this is a more loosely coupled arrangement, enhanced testability, and the ability to inject a different ViewModel for a given View rather than just the default. This is the approach I took.

In App.xaml.cs, I added the following code to the Application_Startup event:

   1: var mainPageViewModel = new MainPageViewModel (new GenerateNumber ());
   2: RootVisual = new MainPageView { DataContext = mainPageViewModel };

First, instantiate the ViewModel, passing in a GenerateNumber object via constructor injection (GenerateNumber is a service class that generates a random number; download the sample code to see it in all it's glory). Then, new up the View, setting it's DataContext to the ViewModel while also assigning the View itself to the application's RootVisual property.

It's setting the View's DataContext to the ViewModel class which essentially "glues" the ViewModel to the View. Also, if we wanted to use a different ViewModel with this View (or have several Views of this type each with a different ViewModel type), it's as simple as assigning each ViewModel type to the View's DataContext. You'd have some difficulty doing that with either of the other ownership approaches above.

Conclusion

This is more or less the bare minimum you must implement in order to adhere to the tenets of the MVVM pattern. I know I left some things out—MVVM frameworks and IoC Containers, for starters. These are platforms that make your life easier, but are not necessarily requirements of implementing the pattern. I know my next step, however, is to start evaluating the options available for each. Future posts should start to steer down those roads.

For more information on the MVVM pattern, take a look at my MVVM Reference post.

Download: MVVMBase.zip


Silverlight to WCF Cross Domain SecurityException

March 31, 2010 12:38

In debugging a fairly simple Silverlight control that calls into a WCF service I ran into an often encountered security exception having to do with cross-domain policy. Silverlight generally does not allow cross domain access unless that access is explicitly granted via a clientaccesspolicy.xml file. Given that I had to read through several sources (see References below) to formulate a solution to this problem, I decided to pull those sources of information together here for future reference.

The Problem

I originally ran into the following dialog when trying to debug my main solution which consists of a couple of nested WCF services and a Silverlight application hosted in a web app (the control makes a call to one of the services which in turn makes a call to the other service).

Here is the exception:

CommunicationException

And the text:

Communication exception was unhandled by user code

An error occurred while trying to make a request to URI 'http://localhost:7249/Service1.svc'. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. This error may also be caused by using internal types in the web service proxy without using the InternalsVisibleToAttribute attribute. Please see the inner exception for more details.

Examining the inner exception, I found:

{System.Security.SecurityException ---> System.Security.SecurityException: Security error.
   at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
   at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4
(Object sendState)
   at System.Net.Browser.AsyncHelper.<>c__DisplayClass2.<BeginOnUI>b__0(Object sendState)
   --- End of inner exception stack trace ---
   at System.Net.Browser.AsyncHelper.BeginOnUI(SendOrPostCallback beginMethod, Object state)
   at System.Net.Browser.BrowserHttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.
HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)}

Given the somewhat complicated structure of the overall application, I decided to take a step back and create a very simple solution that contains a Silverlight app, its associated web hosting app, and a WCF service called “Service1”. I then added a service reference to “Service1” to the Silverlight app and ran the project. That got me back to the above CommunicationException. Good.

The Solution

There’s a lot of potential solutions to this problem (or so it seemed as I was working through it). What worked for me was adding a “clientaccesspolicy.xml” file to the root of the WCF service project. Here’s the contents of that file (and note that the name of the file is important as that is what Silverlight will look for by default):

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers="*">
                <domain uri="*"/>
            </allow-from>
            <grant-to>
                <resource path="/" include-subpaths="true"/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>

This grants access across the board with no restrictions.

When I re-run the application it works as expected.

But, wait, there’s more…

One other issue I had was with the VS Dev Web Server using a virtual path like:

http://localhost:7249/MyService/Service1.svc

This can happen if you have folders inside your solution, for example. The problem is that Silverlight is looking for clientaccesspolicy.xml in the root folder of the service but it now exists in \MyService. Fortunately, there’s an easy way to correct this.

Thanks to Tim Heuer for the solution which is to examine Properties on the WCF service project:

image

The solution then is to remove the folder designation so the virtual path contains the root slash only:

image

 

I re-ran again and everything works.

References

[ Follow me on Twitter ]


Animating the Fill Color of a Silverlight Ellipse

March 28, 2010 18:14

I'm working on this project where I intend to have a Silverlight control that auto-receives push notifications from a WCF service. The client end of the project involves displaying simple "pass" (green light) and "fail" (red light) indicators. Of course I want the indicators to change automatically as new information is received.

This then is a fairly basic tutorial on animating an ellipse to change from a "red" state to a "green" state and back. I'll use a timer to simulate receiving data from a WCF service, alternating the display state between "pass" (green) and "fail" (red). There is, of course, a smooth transition from one color to the other and back, and because of the 2 second timer interval and the 2 seconds it takes for the storyboard animation to complete, we wind up with a strobing ellipse that is constantly changing. This isn't necessarily the end-goal I had in mind, but it did have me captivated for a brief time.

Here is the "Fail" state:

image

And the "Pass" state:

image

The UI Element

For no particular reason I started with a button whose contents consist of a Ellipse and a TextBlock. The initial code for everything inside and including the Grid defined on line 4 came from Mark.NET's post Circular WPF Button Template. I contributed the button itself and the TextBlock, and I changed some of the colors. ;-)

   1: <Button BorderThickness="1" Width="400" Height="125">
   2:         <Button.Content>
   3:                 <StackPanel Orientation="Horizontal">
   4:                         <Grid Width="100" Height="100" Margin="5">
   5:                                 <Ellipse Name="buttonEllipse" Fill="Green">
   6:                                 </Ellipse>
   7:                                 <Ellipse>
   8:                                         <Ellipse.Fill>
   9:                                                 <RadialGradientBrush>
  10:                                                         <GradientStop Offset="0" Color="#00000000"/>
  11:                                                         <GradientStop Offset="0.88" Color="Black"/>
  12:                                                         <GradientStop Offset="1" Color="#80000000"/>
  13:                                                 </RadialGradientBrush>
  14:                                         </Ellipse.Fill>
  15:                                 </Ellipse>
  16:                                 <Ellipse Margin="10">
  17:                                         <Ellipse.Fill>
  18:                                                 <LinearGradientBrush>
  19:                                                         <GradientStop Offset="0" Color="#50FFFFFF"/>
  20:                                                         <GradientStop Offset="0.5" Color="#00FFFFFF"/>
  21:                                                         <GradientStop Offset="1" Color="#50FFFFFF"/>
  22:                                                 </LinearGradientBrush>
  23:                                         </Ellipse.Fill>
  24:                                 </Ellipse>
  25:                         </Grid>
  26:                         <TextBlock x:Name="testResultText" Text="Pass" FontSize="30" Margin="10" VerticalAlignment="Center" />
  27:                 </StackPanel>
  28:         </Button.Content>
  29: </Button>

The Storyboard

I wanted a smooth transition from one color to another when the (mock) test result state changes. I accomplished this by using two Storyboards: one for the green –> red transition, and another for the red –> green. Here's the XAML, which I defined inside the first Ellipse as an Ellipse.Resource:

   1: <Ellipse.Resources>
   2:     <Storyboard x:Name="colorStoryboardGreenToRed">
   3:         <ColorAnimationUsingKeyFrames BeginTime="00:00:00" 
   4:                 Storyboard.TargetName="buttonEllipse" 
   5:                 Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)">
   6:     
   7:             <!-- LinearColorKeyFrame creates a smooth, linear animation between values. -->
   8:             <LinearColorKeyFrame Value="Red" KeyTime="00:00:02" />
   9:     
  10:         </ColorAnimationUsingKeyFrames>
  11:     </Storyboard>
  12:     <Storyboard x:Name="colorStoryboardRedToGreen">
  13:         <ColorAnimationUsingKeyFrames BeginTime="00:00:00" 
  14:                 Storyboard.TargetName="buttonEllipse" 
  15:                 Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)">
  16:     
  17:             <!-- LinearColorKeyFrame creates a smooth, linear animation between values. -->
  18:             <LinearColorKeyFrame Value="Green" KeyTime="00:00:02" />
  19:     
  20:         </ColorAnimationUsingKeyFrames>
  21:     </Storyboard>
  22:     </Ellipse.Resources>

I defined a Storyboard, which is a container for other animation effects, that contains a single animation action: a ColorAnimationUsingKeyFrames, which at it's most basic is a way to transition a UI element's color from one to another. The two important attributes are "Storyboard.TargetName" and "Storyboard.TargetProperty". The former points to the previously defined Ellipse object as the target of this animation and the latter identifies the property we wish to transition. In this case it's Ellipse.Fill. LinearColorKeyFrame defines the smooth transition effect, changing our Ellipse from green to read and back again one frame at a time. Since KeyTime is set to 2 seconds, that's how long the transition will take.

If you want to read up on the basics of Silverlight animation then Animation Overview on MSDN is a great place to start.

Here's the full XAML:

   1: <UserControl x:Class="StrobingEllipse.MainPage"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:     mc:Ignorable="d"
   7:     d:DesignHeight="125" d:DesignWidth="400">
   8:  
   9:     <StackPanel Orientation="Vertical">
  10:         <Button BorderThickness="1" Width="400" Height="125">
  11:             <Button.Content>
  12:                 <StackPanel Orientation="Horizontal">
  13:                     <Grid Width="100" Height="100" Margin="5">
  14:                         <Ellipse Name="buttonEllipse" Fill="Green">
  15:                             <Ellipse.Resources>
  16:                                 <Storyboard x:Name="colorStoryboardGreenToRed">
  17:                                     <ColorAnimationUsingKeyFrames BeginTime="00:00:00" 
  18:                                             Storyboard.TargetName="buttonEllipse" 
  19:                                             Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)">
  20:  
  21:                                         <!-- LinearColorKeyFrame creates a smooth, linear animation between values. -->
  22:                                         <LinearColorKeyFrame Value="Red" KeyTime="00:00:02" />
  23:  
  24:                                     </ColorAnimationUsingKeyFrames>
  25:                                 </Storyboard>
  26:                                 <Storyboard x:Name="colorStoryboardRedToGreen">
  27:                                     <ColorAnimationUsingKeyFrames BeginTime="00:00:00" 
  28:                                             Storyboard.TargetName="buttonEllipse" 
  29:                                             Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)">
  30:  
  31:                                         <!-- LinearColorKeyFrame creates a smooth, linear animation between values. -->
  32:                                         <LinearColorKeyFrame Value="Green" KeyTime="00:00:02" />
  33:  
  34:                                     </ColorAnimationUsingKeyFrames>
  35:                                 </Storyboard>
  36:                             </Ellipse.Resources>
  37:                         </Ellipse>
  38:                         <Ellipse>
  39:                             <Ellipse.Fill>
  40:                                 <RadialGradientBrush>
  41:                                     <GradientStop Offset="0" Color="#00000000"/>
  42:                                     <GradientStop Offset="0.88" Color="Black"/>
  43:                                     <GradientStop Offset="1" Color="#80000000"/>
  44:                                 </RadialGradientBrush>
  45:                             </Ellipse.Fill>
  46:                         </Ellipse>
  47:                         <Ellipse Margin="10">
  48:                             <Ellipse.Fill>
  49:                                 <LinearGradientBrush>
  50:                                     <GradientStop Offset="0" Color="#50FFFFFF"/>
  51:                                     <GradientStop Offset="0.5" Color="#00FFFFFF"/>
  52:                                     <GradientStop Offset="1" Color="#50FFFFFF"/>
  53:                                 </LinearGradientBrush>
  54:                             </Ellipse.Fill>
  55:                         </Ellipse>
  56:                     </Grid>
  57:                     <TextBlock x:Name="testResultText" Text="Pass" FontSize="30" Margin="10" VerticalAlignment="Center" />
  58:                 </StackPanel>
  59:             </Button.Content>
  60:         </Button>
  61:     </StackPanel>
  62: </UserControl>

The Codebehind

In the MainPage constructor I'll add a couple of events to capture the completion of each of the storyboards as well as kick off a timer which will initiate the Ellipse color change.

   1: public MainPage ()
   2: {
   3:     InitializeComponent ();
   4:  
   5:     // we'll change the test result text at the completion of each storyboard
   6:     colorStoryboardRedToGreen.Completed += colorStoryboardRedToGreen_Completed;
   7:     colorStoryboardGreenToRed.Completed += colorStoryboardGreenToRed_Completed;
   8:  
   9:     // simulate receiving results from WCF service
  10:     _receiveResults = new Timer (TimerReceiveResults, null, 2 * 1000, 0);
  11: }

The timer code is as follows:

   1: private void TimerReceiveResults (object state)
   2: {
   3:     // timer can stop for now
   4:     _receiveResults.Change (UInt32.MaxValue, UInt32.MaxValue);
   5:  
   6:     if (_whichStory)
   7:     {
   8:         Dispatcher.BeginInvoke (() => colorStoryboardGreenToRed.Begin ());
   9:     }
  10:     else
  11:     {
  12:         Dispatcher.BeginInvoke (() => colorStoryboardRedToGreen.Begin ());
  13:     }
  14:  
  15:     _whichStory = !_whichStory;
  16:  
  17:     // timer back to normal
  18:     _receiveResults.Change (2 * 1000, 0);
  19: }

I stop the timer while we're processing the event (always good practice), then check a flag to see if we have a "pass" or a "fail". Each Storyboard animation is started with the Begin method, and since I hooked up event handlers to the completion events I'll get a chance to do some more work when those are received.

Here's the code for those event handlers:

   1: void colorStoryboardRedToGreen_Completed (object sender, EventArgs e)
   2: {
   3:     testResultText.Text = "Pass";
   4: }
   5:  
   6: void colorStoryboardGreenToRed_Completed (object sender, EventArgs e)
   7: {
   8:     testResultText.Text = "Fail";
   9: }

Not much to it. I'm just updating the text of the button.

Conclusion

This is one of those things where the code is more a means to an end. In other words, a way to familiarize myself with a few new concepts which I can then take and actually do something useful with. Hope you also get something useful out of it.

Download: StrobingEllipse.zip

[ Follow me on Twitter ]