I’m a dinosaur and it’s possible I’m the only who didn’t know this – But you can use Silverlight DLLs on the full .Net desktop CLR.
See, I’ve been working with Silverlight since Mix07 (March 2007) when we were all excited about Silverlight 1.0 and Silverlight 1.1.
First I was working as a senior .Net consultant on a variety of projects, and for the last 6 months I’ve been working on the Silverlight Toolkit for the evil empire Microsoft.
So believe me when I say – I’m an aging dinosaur to miss something this big. In Silverlight 2.0 you can share DLLs between the desktop and the Silverlight CLR.
I know for sure it wasn’t possible in Silverlight 1.1, and just assumed it wasn’t possible in Silverlight 2.
From all the articles I’ve read in the last few months, It seems I’m not the only one who doesn’t know this:
http://pagebrooks.com/archive/2008/10/11/sharing-code-between-.net-and-silverlight-platforms.aspx
http://petesbloggerama.blogspot.com/2008/12/referencing-non-silverlight-assembly-in.html
http://stackoverflow.com/questions/208123/what-is-the-best-practice-for-compiling-silverlight-and-wpf-in-one-project
All of these fairly recent articles say the same – You need to use the “Add as link” hackery to share source between Silverlight CLR and the full .Net CLR.
Well, Apparently that’s not true. You can add a reference from a full .Net CLR to a Silverlight assembly.
Let’s see how to do that:
1. Let’s start a new Silverlight Application:
Here’s the Solution we got:
2. Let’s add a Silverlight Class Library to hold our Business logic.
Here’s our solution now:
3. Let’s add a class with some business logic.
public class Person
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = UpperCaseFirstLetter(value); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = UpperCaseFirstLetter(value); }
}
private string UpperCaseFirstLetter(string str)
{
if (string.IsNullOrEmpty(str) || str.Length == 0)
return str;
return str[0].ToString().ToUpper() + str.Substring(1, str.Length - 1);
}
public string FullName
{
get
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
}
This class has 3 interesting things in it:
- It has 2 properties called “FirstName” and “LastName” that have a backing field.
- When setting the First and last name, we store these with the first character as uppercase. Property setter business logic.
- We have a property that combines that value of the first two properties. Pure business logic.
4. We’ll add a new WPF project that runs of the desktop .Net CLR. (We could have used the ASP.Net project already in the solution, but I’d like to demo WPF)
5. Now, we’ll add a reference from the WPF project to the Silverlight Class Library.
Using the Business logic in the Silverlight application
First, I’ll add a reference from the Silverlight Application to the Silverlight Class Library.
Next, I’ll add a reference to the Silverlight Toolkit (http://codeplex.com/Silverlight) mainly because we need the WrapPanel.
Next, we’ll add this little form to our default page:
<Border x:Name="LayoutRoot" Background="White" Margin="20" CornerRadius="5" BorderBrush="LightGray" BorderThickness="1" >
<controls:WrapPanel Width="150" >
<TextBlock Width="75" Text="First Name:" />
<TextBox Width="75" Text="{Binding FirstName, Mode=TwoWay}" TabIndex="1" />
<TextBlock Width="75" Text="Last Name:" />
<TextBox Width="75" Text="{Binding LastName, Mode=TwoWay}" TabIndex="2" />
<TextBlock Width="75" Text="Full Name:" />
<TextBox Width="75" x:Name="txtFullName" IsReadOnly="True" InputMethod.IsInputMethodEnabled="False" />
<Button Content="Update full name" Click="Button_Click" />
</controls:WrapPanel>
</Border>
This might look lie a lot, but it’s really not.
It’ just the following form:
It’s only 3 textblocks, 2 editable textboxes, 1 readonly TextBox and a button.
Now let’s add some code to our code behind:
using BL;
namespace SilverlightApp
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Page_Loaded);
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new Person();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
txtFullName.Text = ((Person) this.DataContext).FullName;
}
}
}
Basically, we set the DataContext for the whole form to a Person class and once the button is clicked we manually update the txtFullName.
We could have used Dependency Properties to update this for us, but I’d rather keep this demo simple :)
If we run our application we’ll see the following form:
We’ll put a very sexy and rugged first name and last name.
Next we’ll click the Update button:
Well, that’s pretty straight forward Silverlight. (And if we added Dependency Properties it would have been much better, but this isn’t a Silverlight demo)
Using the Silverlight Class Library / Business Logic in the WPF application
We’ll create a similar yet not identical version of the form in WPF:
<Window x:Class="WPFApp.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<WrapPanel Width="150" >
<TextBlock Width="75" Text="First Name:" />
<TextBox Width="75" Text="{Binding FirstName}" TabIndex="1" />
<TextBlock Width="75" Text="Last Name:" />
<TextBox Width="75" Text="{Binding LastName}" TabIndex="2" />
<TextBlock Width="75" Text="Full Name:" />
<TextBox Width="75" x:Name="txtFullName" IsReadOnly="True" InputMethod.IsInputMethodEnabled="False" />
<Button Content="Update Full Name" Click="Button_Click" />
</WrapPanel>
</Grid>
</Window>
And here’s the pretty almost identical code behind:
using BL;
namespace WPFApp
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new Person();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
txtFullName.Text = ((Person) this.DataContext).FullName;
}
}
}
Let’s run this app.
We’ll put in the ruggedly manly name we used before.
Click the update button.
And yep, the same result – the Silverlight code runs on both the desktop CLR and the SIlverlight CLR.
One picture is worth an entire blog post
How does this even work?
The System.String from Silverlight is a completely different System.String from the desktop .Net. So how does this even work? I’m still looking into this, but before I got the results of my research I thought it’s definitely worth sharing.
This has limitations, probably a lot
Using the full abilities of the Silverlight runtime is obviously not possible from our Silverlight class library when it runs on the desktop. There’s no Application.Current, no dispatcher and a host of other things are missing. This limit us to really sharing only business logic in this method, not UI.
For one, I’m not sure it’s not a good thing that we’re being forced to keep best practices of layer separation.
Source code available at: http://silverlight.net/blogs/justinangel/BlogStorage/SharingCode.zip
-- Justin Angel
Microsoft Silverlight Toolkit Program Manager