I’ve seen a lot of great demos recently of F# being used to create UI for WPF, Silverlight and Windows Phone. In all cases, this has been a very lightweight UI developed for one specific purpose. This is certainly a valid way to develop user interfaces – no need to overcomplicate, but I wondered if there is also a place for a more “grown up” solution, suitable for building complex applications from the ground up, using F#.
I have recently created a number of full blown user interfaces in C# using Microsoft Patterns & Practices Prism library. This library provides the building blocks for building maintainable yet complex user interfaces for WPF, Silverlight, Windows Phone. This is typically achieved through a central “shell” application, and a number of loosely coupled modules which contain parts of the user interface and other services.
I am going to attempt to write an F# silverlight application using Prism. This series will describe that process, but won’t necessarily go into detail about how Prism applications themselves work. I would highly recommend that anyone new to Prism watch Mike Taulty’s excellent series of videos on this subject, and then refer to the MSDN Developer’s Guide to Microsoft Prism.
Step 1 – Create the Shell Application
The first step will be to create the basic shell application, without any content. This will be a silverlight application, using F#, so ensure you download Microsoft Silverlight 4 Tools for Visual Studio 2010. I also installed the F# Silverlight Application Template so that I could create the initial silverlight application easily.
With the above installed, I created a silverlight app project named FSharpPrismShell and a Web application project, named Web.Host as a very basic host for this single silverlight app.
When I tested this basic setup, I discovered that the web application project, whilst allowing me to add my silverlight application in the “Silverlight Applications” tab of project properties, didn’t actually copy this over to ClientBin for some unknown reason. Instead, I just added a manual copy to the pre-build events:
xcopy /Y /R $(SolutionDir)FSharpPrismShell\Bin\Debug\FSharpPrismShell.xap $(ProjectDir)ClientBin
This allowed me to launch my empty silverlight application correctly in the browser.
Step 2 – Make the Shell Application a Prism Shell
Now we need to add Prism to the silverlight application project. I will do this using NuGet. If you don’t have NuGet yet, you should download and install this (in VS2010 use Tools –> Extension manager, then search for and install NuGet Package Manager).
From the Silverlight application project, right click and select Manage NuGet Packages:
Search the online packages for Prism. This should give the above list, so select and install Prism and Prism.UnityExtensions using this.
The contruction of all the elements required in a Prism application is managed by a Bootstrapper. I now need to change my application initialisation to create and run this Bootstrapper, which in turn will create and initialise the shell view (ie the root silverlight control).
So, I’ll firstly modify the default Page.xaml/MainPage combination to create Shell.xaml and Shell.fs.
<Grid x:Name="LayoutRoot" Background="Red"> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom">The red area is from the Shell</TextBlock> </Grid>
namespace FSharpPrismShell
open System
open System.Windows
open System.Windows.Controls
type Shell() as this =
inherit UserControl()
do
Application.LoadComponent(this, new System.Uri("/FSharpPrismShell;component/Shell.xaml", System.UriKind.Relative))
let layoutRoot : Grid = downcast this.FindName("LayoutRoot")
do
()Note that I have made the background red, and added the textbox to allow us to later see what belongs to the shell, and what belongs to the plug-in module. Next I’ll create the bootstrapper PrismBootStrapper.fs:
namespace FSharpPrismShell open System open System.Windows open System.Windows.Controls open Microsoft.Practices.Prism.UnityExtensions open Microsoft.Practices.Prism.Modularity type PrismBootStrapper() = inherit UnityBootstrapper() override this.InitializeShell() = base.InitializeShell() Application.Current.RootVisual <- downcast this.Shell () override this.CreateShell() = new Shell() :> DependencyObject
Finally I need to modify Program.fs to initialise and run this bootstrapper, rather than creating the shell directly itself:
let startup () = let bootStrapper = new PrismBootStrapper() bootStrapper.Run()
I can now build and run this application. Note: I’m observing another weird issue – I seem to need to rebuild all before running each time, otherwise the browser launches with the previous version. Not sure if this is an issue with cassini or a problem with the pre-build steps.
Therefore, I have a working Prism shell application. The next step is to create a plug-in module for this, and wire the two halves together, so that the plug-in appears within this shell.
Step 3 – Create the Basic Plug-in Module
The most basic plug-in for Prism is a module which exposes a single view to be composed within the shell. This is what we will implement today. Create a second silverlight application project and name this one FSharpPrismModule.
Refactor the default Page.xaml/MainPage combination into MainView.xaml and MainView.fs:
<Grid x:Name="LayoutRoot" Background="Green"> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="36">Hello, Modular UI</TextBlock> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom">The green area is from the Module</TextBlock> </Grid>
type MainView() as this =
inherit UserControl()
do
Application.LoadComponent(this, new System.Uri("/FSharpPrismModule;component/MainView.xaml", System.UriKind.Relative))
let layoutRoot : Grid = downcast this.FindName("LayoutRoot")
do
()Now create the discoverable type which implements IModule – this is the core of a Prism module, and will be used to register this module with the shell application later. We’ll call this ModuleDef.fs:
type ModuleDef(regionManager: IRegionManager) =
interface IModule with
member this.Initialize() =
regionManager.RegisterViewWithRegion("ShellContent", typeof<MainView>)
|> ignoreWhat we do here is register a view which Prism will automatically use for any regions defined in the shell named “ShellContent”. Also note that the constructor parameter of type IRegionManager is automatically generated by Prism/Unity when the module is loaded. This is true of any interfaces registered with unity, and is the standard method for providing core services to loaded modules.
This will build and run fine, but we won’t yet see the module for two reasons – it hasn’t been registered with Prism, and we haven’t defined a region in the shell into which its UI should be placed.
Step 4 – Register the Module with the Shell
Firstly we need to update Shell.xaml to add a control which will act as the placeholder, into which Prism will inject the view from our module:
... xmlns:Regions="http://www.codeplex.com/prism" ... <Grid x:Name="LayoutRoot" Background="Red"> <ContentControl Margin="20,20" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Regions:RegionManager.RegionName="ShellContent" /> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom">The red area is from the Shell</TextBlock> </Grid>
Now we need to create a module catalog, which is a xaml file defining the list of modules which Prism should make available as plug-ins. Note that this can also be achieved programmatically within the bootstrapper. We’ll create a file called modules.xaml and include this as content within the shell project.
<Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism"> <Modularity:ModuleInfo Ref="FSharpPrismModule.xap" ModuleName="FSharpPrismModule" ModuleType="FSharpPrismModule.ModuleDef, FSharpPrismModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> </Modularity:ModuleInfo> </Modularity:ModuleCatalog >
Note that we are actually including the FSharpPrismModule xap file here, and specifying the type to be loaded within this xap file. Prism works out all the complexity of one xap file (the shell) including another xap file (the module) on our behalf.
In order for this to be found, I will need to update the prebuild step to xcopy this new xap file into the ClientBin directory
xcopy /Y /R $(SolutionDir)FSharpPrismModule\Bin\Debug\FSharpPrismModule.xap $(ProjectDir)ClientBin
xcopy /Y /R $(SolutionDir)FSharpPrismShell\Bin\Debug\FSharpPrismShell.xap $(ProjectDir)ClientBin
This said, I was initially banging my head against a wall when the runtime continued to fail to load FSharpPrismModule.ModuleDef. Eventually (with help from .NET Reflector) I realised that the generated module had the version number 0.0.0.0, so I added an AssemblyInfo.fs file to the module project:
open System.Reflection
open System.Runtime.CompilerServices
open System.Runtime.InteropServices
// The following GUID is for the ID of the typelib if this project is exposed to COM
[<assembly: Guid("06725367-D5EE-4D48-BC6B-827211C133A6")>]
[<assembly: AssemblyVersion("1.0.0.0")>]
[<assembly: AssemblyFileVersion("1.0.0.0")>]
() In order for the module itself to be loaded, we need to ensure that the bootstrapper knows how to find the modules. In order to achieve this, I added another override to PrismBootStrapper.fs:
override this.CreateModuleCatalog() =
let catalog = ModuleCatalog.CreateFromXaml(new Uri("modules.xaml", UriKind.Relative))
catalog :> IModuleCatalog
So finally, Prism can find our module, load it, and determine that it can provide a view for the placeholder control within our shell. If we now run this application, the result will be:
That’s enough Prism for now. What we have is a very simple composed user interface written entirely in F#. It isn’t very useful yet, as we only have one module, we don’t have any additional services, or communication between the shell and the module or between modules. I’ll endeavour to expand this into something more useful in the near future, but for the moment this is a reasonable start.
Source code is available for download from bitbucket.




