10 Minute Tutorial - Silverlight: Building a Silverlight application with MSBuild (C#)
“If you build it they will come”
Don’t know about that, but if you use MSBuild, it’ll sure make everyone’s life a hell of alot easier.
As of .NET 2.0, Microsoft provides developers with a build engine, called MSBuild, as part of the runtime distribution. In this tutorial, I will walk through hand-coding a MSBuild build file for the sample code of my application framework Silverlight tutorial.
Prerequisites
- All prerequisites defined in the Silverlight application framework tutorial.
- .NET Framework 3.5.
And, for this tutorial only, we will be reusing the sample code from the Silverlight application framework tutorial.
QuickStart
If you want to see the end result of this tutorial and you have installed all the prerequisites, then please download the ZIP file below, unzip it and open the smiley.html file.
- Silverlight MSBuild Sample (you may read this software’s license here)
Lesson
Step #1: Create the application files
The Silverlight application framework tutorial used pure XAML, but this time we will put code behind it (albeit, not very much code
). First, create a file named App.xaml and put this code in it:
<Application
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
Now, we need the C# code that sits behind it. Create a file named App.xaml.cs and place this code in it:
using System.Windows;
namespace SmileyApplication
{
public partial class App : Application
{
public App()
{
InitializeComponent();
}
}
}
For those of you who know WPF, this should look familiar. The Application class will handle all application level events and setup code for our Silverlight Application. Now, let’s wire these two classes two each other
App.xaml
<Application
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SmileyApplication.App"
Startup="Application_Startup">
</Application>
App.xaml.cs
using System.Windows;
namespace SmileyApplication
{
public partial class App : Application
{
public App()
{
InitializeComponent();
}
void Application_Startup(object sender, StartupEventArgs e)
{
// Load the main control
this.RootVisual = new SmileyControl();
}
}
}
I won’t dig into much of the XAML now, but using the x:Class attribute allows us to associate the Application XAML with our Application C# class, we create an association between App C# class and the Application defined in the XAML. I’ve also added an event handler for the Startup event that sets the RootVisual of the application to a new instance of a SmileyControl class, which we will define next.
Step #2: Create the content files
Now, let’s create the files that represent our content. The previous smiley.xaml file used a Canvas as its root element, however now we need to wrap the Canvas in a UserControl, the Microsoft-preferred basis for a Silverlight application. Create a file named SmileyControl.xaml and put this code in it:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="SmileyApplication.SmileyControl">
<Canvas x:Name="Smiley" Width="200" Height="200">
<Ellipse x:Name="Ellipse" Width="200" Height="200" Stretch="Fill" Fill="#FFFFFF00" />
<Ellipse x:Name="LeftEye" Canvas.Left="50" Canvas.Top="50" Width="20" Height="20" Stretch="Fill"
Fill="#FF000000" />
<Ellipse x:Name="RightEye" Canvas.Left="130" Canvas.Top="50" Width="20" Height="20" Stretch="Fill"
Fill="#FF000000" />
<Path x:Name="Mouth" Stroke="Black" StrokeThickness="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="50,130">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="100,100" RotationAngle="45" IsLargeArc="False" SweepDirection="Counterclockwise"
Point="150,130" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
</UserControl>
And, now for the code. Put the code below in a new file named SmileyControl.xaml.cs:
namespace SmileyApplication
{
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Shapes;
public partial class SmileyControl : UserControl
{
public SmileyControl()
{
InitializeComponent();
}
}
}
If you think that this UserControl does nothing…then you’re right :). But, I wanted to keep this part simple because we still have to tackle MSBuild.
Step #3: Create an MSBuild project file
Now onto the build! First, create a file named SmileyApplication.csproj. In this file, declare a Project, like so:
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
</Project>
As you can see, MSBuild uses XML for its build definition files. The Project element, the most top-level element in a MSBuild file, encompasses EVERYTHING: targets, property definitions, input file items, etc. The ToolsVersion attribute tells MSBuild to use the .NET 3.5 version of .NET toolset (it also sets the $(MSBuildBinPath) and $(MSBuildToolsPath) variables). By setting the DefaultTargets attribute, we make MSBuild execute the “Build” target first (as you will see later, Microsoft defines this target for us).
And with that, we have a valid (yet empty) MSBuild project. Now, let’s add something to it.
Step #4: Set compilation options
Before we can compile, we need to tell MSBuild how we want our code compiled by using the properties below:
<PropertyGroup>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<SchemaVersion>2.0</SchemaVersion>
<NoStdLib>true</NoStdLib>
<NoStdCfg>true</NoStdCfg>
<RootNamespace>SmileyApplication</RootNamespace>
<AssemblyName>SmileyApplication</AssemblyName>
<OutputType>Library</OutputType>
<OutputPath>ClientBin</OutputPath>
</PropertyGroup>
Some of these properties deserve a quick explanation:
- NoStdLib - prevents the inclusion of the .NET runtime version of mscorlib.dll
- NoStdCfg - prevents the inclusion of the configuration file assembiles (unlike WPF or WinForm applications, Silverlight applications don’t use .config files)
- <OutputType>Library</OutputType> - All Silverlight applications get deployed as libraries
Now, we just need something to compile.
Step #5: Add the input files
Let’s add the code and XAML files to the project:
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:MarkupCompilePass1</Generator>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<Page Include="SmileyControl.xaml">
<Generator>MSBuild:CompileXaml</Generator>
</Page>
<Compile Include="SmileyControl.xaml.cs">
<DependentUpon>SmileyControl.xaml</DependentUpon>
</Compile>
</ItemGroup>
So, the first ItemGroup contains build instructions for the applications files, while the second ItemGroup contains build instructions for the custom UserControl. While most of this requires no explanation, take note of the ApplicationDefinition and Page elements. Both these elements signal MSBuild to generate code for the partial classes defined in those files and embed the XAML resources into the final assembly.
Note: In some projects, you may find a SilverlightPage element used instead of a Page element. The first version of the Silverlight tools for Visual Studio used this, but no longer does. So, if you try to compile a project that uses a SilverlightPage task directly with MSBuild, you will get an error that says the InitializeComponent() function dosen’t exist. However, if you use the Silverlight tools for Visual Studio, Visual Studio will make an exception and treat it like a regular Page task. This one caused me hours worth of headache, and I’m not the only one, so I don’t want the same to happen to you. In conclusion, when building from the command line, just replace any SilverlightPage elements with the Page element and you won’t have any problems.
Step #6: Reference the Silverlight assembiles
To successfully compile, the project will also need to reference the Silverlight libraries. Those references will need their own ItemGroup:
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="system" />
<Reference Include="System.Windows" />
</ItemGroup>
Nothing amazing here. Keep in mind however, that the mscorlib and system references point to Silverlight specific assemblies installed with the SDK.
Step #7: Deploy
And now, onto our final property group, this time, declaring properties for deployment:
<PropertyGroup>
<SilverlightApplication>true</SilverlightApplication>
<SilverlightAppEntry>SmileyApplication.App</SilverlightAppEntry>
<SilverlightManifestTemplate>AppManifest.xml</SilverlightManifestTemplate>
<GenerateSilverlightManifest>true</GenerateSilverlightManifest>
<XapOutputs>true</XapOutputs>
<XapFilename>SmileyApplication.xap</XapFilename>
</PropertyGroup>
Again, a quick description of the most interesting properties:
- SilverlightAppEntry - The fully qualified application class name
- SilverlightManifestTemplate - The template file MSBuild should use to generate the application manifest
- GenerateSilverlightManifest - Tells Silverlight to generate a manifest and put it in the resulting XAP file
- <XapOutputs>true</XapOutputs> - Tells MSBuild to generate a XAP file
- <XapFilename>SmileyApplication</XapFilename> - Name of the resulting XAP file packaged by MSBuild
A Silverlight XAP uses the zip file format to wrap all the necessary application assemblies and an application manifest into one neat package. The application manifest defines every assembly in the package and also tells Silverlight which assembly to start with. Let’s make one right now. Create an application manifest file named AppManifest.xml and put a Deployment definition in it, similar to this:
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Deployment>
You may think this too simple, but remember this is a TEMPLATE file. MSBuild will insert the attributes and elements necessary to define each of our assemblies and the entry points based on the information in the build file and then package that in the XAP file for us. Pretty nice, huh?
Step #8: Import Silverlight build tasks
Thankfully, we don’t need to execute any of the tasks for file compilation, manifest creation, XAP packaging ourselves because Microsoft has already made the build targets to do so. We just need to import those targets and then run the “Build” target Microsoft has defined (remember, we set the DefaultTargets attribute of the Project element to “Build”, so it will run that target automatically). So, let’s add the Import task to the project:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight\v2.0\Microsoft.Silverlight.CSharp.targets" />
If you get really bored one day, open up Microsoft.Silverlight.CSharp.targets. It makes for a very interesting read.
Step #9: Build it
To run the build, execute this command in your working directory, assuming you have the .NET 3.5 SDK installed in the default directory on your workstation:
"C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" SmileyApplication.csproj
This should create a directory named ClientBin that should contain these files: AppManifest.xaml, SmileyApplication.dll and SmileyApplication.xap.
Step #10: Run it!
Before you open the HTML file, remember to open createSilverlight.js and change the “source” parameter to “ClientBin/SmileyApplication.xap”. Now, open your HTML file and you should see a familiar face.
Conclusion
This tutorial walked through creating a basic MSBuild file for a Silverlight application. Granted, you may not see the point in simply recreating an existing project, but having an MSBuild file opens up a whole new realm of productivity and flexibility. Developers can spend less time coding up small utilities, worrying about command line switches to shell scripts and mucking around in the files system and spend more time using and writing tasks to manipulate their build assests quickly. Also, Visual Studio can open any valid MSBuild file, so a developer can go back and forth between hand-tweaking their build and then reusing that as a Visual Studio project. Although NAnt served me well for years, it brings me solace to see Microsoft recognizing the need for a standalone build engine and then meeting that need with the high quality solution developers expect from the .NET eco-system. For further reading, check out the links below:
An Introduction to MSBuild on The Code Project



