If you are like me, when you first heard about Silverlight, you thought this is fantastic, the WPF application you just developed can be taken, and with a few changes to support asynchronous loading, you've got an Internet accessible application! Well, think again! That's not how it works. Although it is supposed to be a subset of WPF, there is a lot of core WPF functionality that is either not there or operates fundamentally differently under Silverlight.
Most Microsoft people recommend that if you are starting a new application that you develop the Silverlight version first, and later move that to WPF. Unfortunately, in our case we had already developed the Virtual Tour Viewer software for WPF, well before Silverlight was on the scene. To some extent I still think it's better start with WPF because ultimately, hopefully, Silverlight will pick up more WPF functionality, and in my opinion the WPF functionality, such as triggers, is generally easier to code with than Silverlight equivalents.
To prove that it is possible, below are images of our virtual tour viewer running in WPF and Silverlight:
| WPF | Silverlight |
| |
So, having built your application how do you convert it to Silverlight? Well, the first thing is to create a new solution. I have one solution for the Silverlight version and another one for the WPF version. So I ended up with a folder structure like this:
The Virtual Tour Viewer folder is the WPF version. Within each solution the projects are replicated, with Silverlight versions and WPF versions of the project (where appropriate, there is some projects that are only in WPF in the Virtual Tour Viewer). This can be seen below:
| WPF | Silverlight |
| |
So, what code can you share between Silverlight and WPF? Initially, I was hopeful of being able to share both the xaml and .cs files. However it became clear very early on that it was not going to be possible due to the number of differences in the xaml capabilities between Silverlight and WPF. In the end there was not a single XAML file that I was able to share. A number of people have also reported difficulties in dealing with linked XAML files in Visual Studio, although in my testing I did manage to get it to work, but I didn't do any serious testing so that may have been a fluke.
So in the end I share the code behind .cs files but not the XAML files. I am also able to share all the data access layer .cs files.
The one important point is that you need to make sure that each assembly has exactly the same name on WPF and Silverlight. This is important for xaml references
e.g:
xmlns:Controls1="clr-namespace:OlsonSoftware.Windows.Controls;assembly=OlsClassLibUI"
This reference can then be used across both WPF and Silverlight if the assembly name is kept the same. This can be done by right clicking on the project and selecting properties:
There are two ways you can manage each project:
Both projects in a single folder
This is the simplest way. All you do is create a second project (in my case a Silverlight class library), in the same folder. Visual Studio automatically sees all the files in that folder and you can simply exclude or include any files to get the files you require.
A problem with this approach is that it doesn't work where you have custom controls. The xaml for a custom control is stored in a file called themes/generic.xaml. Because your xaml will almost inevitably be different between WPF and Silverlight, it isn't possible to share that file between the two projects when they are both in the same folder, as you need two separate generic.xaml files.
Another problem is that both the WPF and Silverlight solution want to build the same folder, e.g. Debug. The work around for this to set the output path in the project properties:
Projects in separate folders using linked files
This is the way that I ended up choosing for almost every project in the solution. It gives much more flexibility the previous option, because I can now have a clear separation between files that are just used for Silverlight and the WPF project.
So, how do you do this? Well this time you create a new class library project in a new folder. This leaves you with an empty project. Now you have two projects, one for Silverlight and one for WPF, each in their own folder.
For any files that you need to share, you can use Visual Studio's Add Existing Item. The difference is that instead of clicking Add, you click the drop down menu beside it and Add as Link:
Normally, Visual Studio will copy the file to your project folder. Using this feature it does not do this and simply references the file in the other folder. This way any changes you make for the other project are automatically reflected in this one.
This opens up another problem, there are a number of features available only in WPF, or in Silverlight that you may wish to use. With this linked file, if you use any of these features it may not compile in both projects. The solution to this is to use the conditional compiling feature. Any code that does not compile in Silverlight can be surrounded by these commands:
#if !SILVERLIGHT
[code goes here]
#endif
Of course it is the opposite situation where the code does not compile in WPF:
#if SILVERLIGHT
[code goes here]
#endif
Unfortunately you'll find yourself using these far more than you would wish because there are so many differences between WPF and Silverlight. I have resolved some of these issues by developing a WPF compatibility library to provide WPF functionality on Silverlight such as routed commands, routed events, FlowDocumentViewer, navigation etc. We will be making it available as an open source library in the near future.
With this project setup or you can add files specific to Silverlight and WPF in their respective project folders as normal.
Sharing user controls
To share the code behind for your xaml files, create your user controls as normal in WPF. e.g:
Then create the same control in Silverlight and delete the .cs file from the project. Then you can add the existing CS file from the other project. Unfortunately, when you are using linked files as code behind, Visual Studio doesn't display them as a child of the xaml file, even though it is correctly indicated in the project. So this is what you will end up with:
To share the xaml code between both projects you just have to copy and paste, unfortunately.
Sharing custom controls
Custom controls have a single xaml file themes/generic.xaml. You need a separate file in both the WPF and Silverlight projects. The code for each control can be linked to as described above.
So that is an overview of how we solved project management with Silverlight and WPF. We hope that it is helpful and adequately explains how we have done this.
If you want to check out the WPF and Silverlight versions of the Virtual Tour Viewer, I'll be posting a new blog entry when I make them publicly available in the next week or so.
…Stefan