Note for future readers: These series discuss WPF and Silverlight versions that are current stable versions at the time of this writing – WPF 3.5 and Silverlight 3.
These are ongoing series of posts on the subject of WPF/Silverlight compatibility. New posts will be added to the Table of Contents post as they are written so bookmark that post or just subsribe to my RSS feed.
Silverlight is a subset of WPF. This is the short official version. In reality this might be close to true featurewise, but as developers of cross-platform (here and later in these series "platform" should mean WPF or Silverlight) solutions we care not only about what benefits some feature provides to the end user, but about it's API for us (developers) and quite often even about it's internal implementation.
This is how it looks to me at this point (please excuse my artistic “skills”):
At the time of this writing I'm in the middle of converting of one of our WPF charting controls to WPF/Silverlight compatible code. It took me more than a week of intense work just to get it to compile and display something under Silverlight and I have yet a lot to do before everything works as expected. During this journey I've been writing down all the differences I encounter and I'm going to share my findings, solutions and workarounds in these series.
There's great high-level conceptual information on the differences in Silverlight documentation and Wintillect’s whitepaper, but it's often the details that cost (or save) you time and cause headaches.
I assume that anyone reading this plans to develop a cross-platform application or control or at least wants to be prepared for the future. Such project could be approached in two ways:
- Develop cross-platform project right from the start
- Develop for one platform and adopt for the other later.
Develop cross-platform project right from the start
This is the way to go if your only concern is the quality of the end result. Unfortunately in real life quality is often offset by the other very important metric – time to market. It's hard to argue that one will see some real tangible results faster when concentrating all attention on one platform.
Develop for one platform and adopt for the other later
As was said above you will most likely go this route. Here you have another 2 options: start from WPF or Silverlight. There might be some business, religious or other reasons to start from one or the other, but we will set all of these aside and look only on technical aspects.
From my point of view life would be easier if you start with Silverlight and ease into WPF later. With that said it’s not necessary that easier equals better. You will most likely settle for lowest common denominator which could be minor problem in most cases but could mean a lot in some. I’ll post a separate part on one of the differences were taking a special route for WPF version could make a difference.
If you start with WPF, however, you will most likely end-up doing quite some rewriting when time comes to step into Silverlight, but you wont have to break what’s working just to get a better performing WPF version (considering there’s a reason to care about performance in your project).
There are several methods to facilitate code reuse between Silverlight and WPF versions of your poject while handling the differences at the same time. Personally I use linked code files for common parts and two methods to tame the differences – preprocessor directives (where just a small portion of code differs) and partial classes (for serious differences).
Linked code files in Visual Studio
To add a linked code file to your WPF or Silverlight project in Visual Studio you just go to Project –> “Add Existing Item…” navigate to your other (Silverlight or WPF) project directory, locate the file and use “Add As Link” item in the “Add” dropdown button.
Preprocessor (compiler) directives
When you have a small amount of code differing between the versions you can use preprocessor directives to include one or the other chunk of code depending on the type of project being compiled. For your convinience Silverlight project template predefines “SILVERLIGHT” constant. You can define something shorter and more mistyping proof but I find it’s safer to go with this default (you never know when and how you are goint to reuse this code). Here’s how you go about this:
1: private TextBox _dataTextBox;
2: public override void OnApplyTemplate()
4: #if SILVERLIGHT
5: _dataTextBox = this.GetTemplateChild("PART_DataTextBox") as TextBox;
7: _dataTextBox = this.Template.FindName("PART_DataTextBox", this) as TextBox;
While preprocessor directives are ok for ducttaping small differences, the code becomes quite difficult to digest pretty quickly. Enter partial classes. You define common code in one partial class file and link that file from one project to the other. Then you create a separate partical class files for methods that differ in each project.
Here’s how a simplistic Visual Studio solution implementing this approach looks like:
MyControlWPF is a WPF project. It contains MyControl.cs file which contains a partial MyControl class. That file contains code that is identical for both WPF and Silverlight. It is added to MyControlSL (our Silverlight project) as link. So there’s only one copy of this file to maintain and it’s used in both projects. MyControl.WPF.cs and MyControl.SL.cs on the other hand contain members of MyControl that differ in implementation between WPF and Silverlight.
What about XAML?
Unfortunatelly there’s no standard way to use preprocessor directives or something like that to isolate the differences. There are some custom hacks however. While this is definitely cool from enthusiasts point of view or when you have really large XAML files to maintain, I think it’s easier to simply handle 2 files where differences are unavoidable. So you either make your XAML work for both WPF and Silverlight and link that XAML file from one project or you maintain 2 different files.
That’s it for part 1. Starting with the the next part I’ll show the differences I’ve stumbled upon and a ways to solve or workaround them. Stay tuned…