There seems to be lots of interest in porting Windows Presentation Foundation (WPF) and WinForms application to Silverlight, and occasionally from Silverlight applications to WPF as well. Because of this, I wanted to see how feasible it was to maintain the bulk of my code in a single code-base, and provide just the code- glue to support the framework. I happened to have a cool looking erlight application, so I decided to convert this to WPF in the hope that it would give me an insight to the tasks of porting between the two frameworks. It did.
I had to come to terms with the reality that one cannot hope or expect to share 100% of the code base by merely inserting conditional compilation preprocessor directives in any application more complex than the’ Hello World’ application.
First Magic Ingredient: Preprocessor Directives
Preprocessor directives are the basic way of maintaining a single code base, whilst porting to WPF , WinForms and Silverlight. Preprocessor directives are basically just conditional statements within your code. They are different from the regular conditional statements in a sense that they don’t execute at run time. They are interpreted by the compiler before it performs the compilation passes. If a block of code is in a conditional statement that returns true, then that block will get compiled: If it returns false, then that block of code doesn’t get compiled. For example, In the following code I am telling the compiler that if the code file that is being compiled is in a Silverlight project then compile this section of the code otherwise compile the other section of the code that would be used with regular .net applications such as WinForms, or WPF.
1 2 3 4 5 |
#if SILVERLIGHT IPopupWindow popWindow = new ChildWindowEx(); #else IpopupWindow popWindow = new WPFPopupWindowEx(); #endif |
Visual Studio is very smart when it comes to conditional preprocessor directives. It will automatically gray out the inapplicable code block based on the condition. For example, if this code was part of the Silverlight project then the else block will automatically be grayed out.
Second Magic Ingredient: Linking to a file
The second Magic Ingredient is linking files within projects. We are all used to right clicking the project file -> Add -> New Item… to add a new item of course: But if you look right below that, there is another option that says “Existing Item…”. Now you can click that and select a file from your WPF project to share in the Silverlight port of that project. However, that isn’t really what we want, because it creates a copy of that file into the Silverlight project. But if you click the drop arrow of that Add split button on the “Add Existing Item” dialog, instead of clicking the add button, then you will reveal another option of “Add as a Link“: That is the one you want to click. That will just create a short cut of that file in your current project. If you combine this technique with preprocessor directives you can then share code between two different projects targeted for two different platforms.
Maintaining the balance:
When you are aiming to achieve a single code base, you shouldn’t compromise your code readability. You will always come across a code file where no common code would be possible. This code would become unreadable if you just used conditional compilation. If we are using single code base to make applications more maintainable then this would defeat the purpose. We’re aiming to decrease the time to update or make changes to applications, and to reduce bugs by sharing the same logic. If the code isn’t readable then, for sure, it will not be easy to maintain. On the contrary, it will take more time to make changes.
So in the case where a file has a considerable difference between platforms there are two possible solutions.
- You could split the file into two. One half will contain the code (logic) common to both, which will be easily shared between the two platforms, and the other half will have a version for each platform.
- You could subclass the original file and create two separate versions of the derived class one for each platform.
Which approach you use depends on which method provides the best maintainability and portability of the code, both now and in the future. I have used both techniques.
Other Challenges
Even though one won’t be able to share 100% of the code base, we can on average share good 70% if the application is well architected.
There are ways of increasing the proportion of common code by careful planning. You will, for example, notice if you are going to Silverlight to WPF that there is no ChildWindow or BusyIndicator classes. Both of these classes are very common in Silverlight application. One alternative is to use Window class, spawn a new window, in WPF to replace both of those. This is the natural solution in the Windows environment. However, if you need exactly the same behavior as in Silverlight for ChildWindow and BusyIndicator classes, then it’s actually not that difficult to create those control from scratch in WPF. And if you port between Silverlight and WPF regularly, than it might be good idea to create those controls as they would come handy for future projects. For my demonstration I need exactly this ChildWindow and BusyIndicator functionality in Silverlight for my WPF conversion, so I ended up creating those controls.
Although you can achieve a lot by bringing the two versions to a common denominator, you may lose functionality if you take this too far. As an example of this, my Silverlight application uses some beautiful transitions between screens. Those are custom transition made possible via the projection plane properties in Silverlight. As you know, there is no projection plane in WPF. They have something more powerful, which is ViewPort3D. So in such cases where the other framework offer better and more powerful options then I think one should not be afraid to deviate a little and take advantage of those. In other words, it’s better to not have the conversion so exact that the applications are 100% identical if one can exploit the unique features of the platform to make the converted application better than the original. In this case I believe that the 3D transitions that I used in converted WPF application are better than the transitions in the Silverlight application.
Load comments