Woo Hoo! We just released the first drop of Prism 2.0 on our CodePlex site here.
For this first iteration we’ve focused solely on the "multi-targeting" aspects of Prism (UI composition and modularity support will follow soon though). By multi-targeting we mean the ability to build applications that can provide both a WPF desktop and a Silverlight RIA experience by sharing and re-using code and components between the WPF and Silverlight environments.
[As a side note, we’ll probably have to pick another name for this feature soon since it turns out that, to a sizeable number of folks, "multi-targeting" implies targeting different versions of the .NET Framework – see here for example. So to avoid confusion we’ll likely have to pick another name for this – maybe multi-headed, multi-experience apps, or something? Vote now on your favorite, or suggest one and win a prize!]
To get started, you’ll need to have the following bits installed:
- Visual Studio 2008 SP1 (here).
- .NET Framework 3.5 SP1 (here)
- Silverlight Beta 2 and tools (here)
- Silverlight Unit Testing Framework (here)
- Visual Studio 2008 SDK (here) – This dependency is temporary and will go away soon (unless you want to recompile or debug the Project Linker).
Finally, download the Prism 2.0 source code zip file and the Project Linker msi from here.
The project linker is a Visual Studio extension that helps to maintain links between projects – more on this below. To install the Project Linker, open up a Command Prompt as an administrator and run the msi like this:
msiexec /i CompositeApplicationGuidanceProjectLinker.msi
The Prism 2.0 zip contains a multi-targeting quick start, as well as the source for the project linker, the acceptance testing library, and a Visual Studio template. Once you’ve unzipped everything, copy the unit testing framework libraries into the ~\LIB\Silverlight\UnitTestFramework folder and you should be all set. You can also install the Visual Studio templates, though this isn’t required.
Running The Quick Start
The multi-targeting quick start contains a simple application that provides a desktop experience (on WPF) and a RIA experience (on Silverlight). The quick start includes projects for the app, associated unit tests, and a small library for displaying simple pie charts. The projects for the desktop version are in the DotNet folder and the projects for the RIA version are in the Silverlight folder.
To run the desktop version, right click on the RealEstateListingViewer.Net project in the DotNet folder, select ‘Set As StartUp Project’ and press F5.
To run the RIA version, right click on the RealEstateListingViewerHost web project in the Silverlight folder and select ‘Set As StartUp Project’. Next, right click on the RealEstateListingViewerTestPage.html page and select ‘Set As Start Page’, and then press F5.
Not surprisingly, the UI for both versions of the app are pretty much identical.
Accounting for the Differences & Similarities
The app doesn’t really do much, it’s really just meant to illustrate the code sharing approach to account for the differences and similarities between the WPF and Silverlight environments. This approach uses links to share code source files between WPF and Silverlight projects.
Each project in the quick start solution manages all of the references, resources and code specific to the WPF or Silverlight target environments. Common code that is shared between the two environments is linked from one project into the other project so it gets compiled into each target.
This technique relies on the Visual Studio Add As Link feature that allows you to set up links between files in different projects by selecting the ‘Add As Link’ menu option to add a link to an existing file in another project.
If you drill into the projects in the quick start solution a little you’ll see that the app is split into a number of folders for Images, Models, Services and Views. In the WPF version of the app, the vast majority of the files in these folders are just links to the files in the Silverlight projects. In fact, there are very few WPF or Silverlight specific files – only the root App, RealEstateListingView and Property Service classes are specific. Let’s dig into these a little.
The App class accounts for differences in the hosting and initialization model for SL and WPF. Differences here are pretty minor but since this class is generated and typically does not contain much code it is easier to keep the WPF and Silverlight specific classes than to abstract Application into a common class.
The RealEstateListingView class accounts for the fact that in WPF the UI is defined as a Window, while in Silverlight it’s defined as a UserControl. The actual differences in the UI are minor and we could have made the WPF view a UserControl too and maybe shared the view across WPF and Silverlight. This example does illustrate an important point though.
For relatively simple views we expect that it will be possible to share views between WPF and Silverlight – and we hope to provide extensive guidance on exactly how to do this. But it’s likely that apps will want to take advantage of WPF or Silverlight specific UI features (e.g. 3D, XPS or trigger support in WPF, Visual State Manager in Silverlight, etc). In these cases, you’d want a WPF or Silverlight specific ‘skin’ for your application (or more likely, for certain key parts of it).
The PropertyService class is responsible for retrieving details of a particular house, including an image of it. The PropertyService class implements the IPropertyService interface, which is how the service is integrated into the common core of the application, but its implementation is actually split over two partial classes. You’ll see that both projects contain a common PropertyService.cs file. This defines the GetProperty method of the service. This implementation is shared between the WPF and Silverlight projects and so the PropertyService.cs class is linked.
But you’ll also see that the Silverlight project contains a PropertyService.Silverlight.cs file, while the WPF project contains a PropertyService.Wpf.cs. Each of these files defines the specific behavior for the WPF or Silverlight environment and so these files are not shared between projects. These environment specific files define the GetImage method. The implementation of this method is slightly different for WPF and Silverlight – for WPF, the image is loaded directly from disk, for Silverlight, the image is downloaded from the web server.
This is a trivial example but it does illustrate another important point. It’s more than likely that an application will sometimes have to employ slightly different strategies when running in WPF and Silverlight environments. Splitting an implementation of a service over two partial classes is one way to cleanly account for these differences. Note that the service interacts with the rest of the core (common) application through a common interface, so this technique is really just a way to factor implementation level details into files that are conditionally compiled for each environment.
This technique is good for accounting for small scale implementation differences between WPF and Silverlight. In other cases you might find that the differences between the environments are coarse grained and don’t fit into this pattern well. This is where Prism’s support for modules and composition comes in.
Let’s take data access an an example. In the WPF case, an offline-capable app will likely be running in full trust and will retrieve most of its data from the local file system and/or a local database and retrieve/sync data over the network only occasionally. In the Silverlight case, the app is running in a sandbox and will most likely access it’s data over the network and only use local isolated storage for simple caching.
The differences between these two strategies are probably too big to do cleanly using partial classes, so you’d likely implement them as service agents in different modules with a common interface into the core/common part of the app. Prism will also support this coarser grained composition and allow specific strategies to be plugged in easily and automatically.
The Project Linker
As you can see, the approach we are taking for multi-targeting is based on structuring your application and module code into multiple linked projects. This is a low tech but effective approach that allows common code to be compiled against two target environments.
Manually maintaining the links between files in multiple projects using Visual Studio is possible but it can be time consuming and error prone, so Prism 2.0 provides a ‘Project Linker’ tool that tries to make it easier to create and maintain the links for you. With the project linker you can specify which projects are related and it will then make sure that common files that you add to one project will be linked into the other project automatically.
With the Project Linker installed as above, you can use it create and maintain links between a source project and a target project so you can auto link shared code that is common to Silverlight and WPF. Let’s see this in action…
Right click on the RealEstateListingView.Net solution and click the ‘Add Project Link’ menu item. This is the target project – the project into which we want to create links. The Project Linker project selector dialog is shown.
We want to link this project to the Silverlight RealEstateViewProject so select that project as the source project and click OK. The project file is now updated to include information about the relationship between the two projects.
The quick start already has all of the relevant links in place so the Project Linker has not really kicked in yet. But, if you now add a new file to the Silverlight project, say in the Services folder, the file will automatically get linked into the equivalent folder of the WPF project. Add a new file called ‘MyNewService.cs’ and you will see a link show up automatically in the WPF project.
Now, say you wanted to add a file to your project but didn’t want it linked into your WPF project. The Project Linker uses filters to decide when to auto create links. By default, if you add a file with a .Silverlight.cs or a .WPF.cs suffix, as in the quick start, they will be ignored by the project linker and won’t get auto linked.
The functionality for the Project Linker is fairly basic at the moment. We want to validate that the code sharing through links approach works before we invest too much effort in it. To make it more usable and useful we’d like to add the ability for the developer to see what projects are linked and allow them to change and remove links.
We’d also like to add the ability for the developer to define custom filter rules so that they can decide what convention is used to create or prevent auto link files. The default rule is based on the .Silverlight.cs or .WPF.cs suffix, but you may want a different naming convention, or use a certain folder structure to contain specific or shared files regardless of the file names.
If you have any feedback on Prism 2.0, please let us know. We’re eager to find out whether the code sharing approach is a workable solution (in the unfortunate absence of binary compatibility) and what folks would like to see in the Project Linker. We’re working on the next iteration now, which focuses on module loading, so expect another drop in a couple of weeks.