Monday, June 16, 2014

Creating real world WPF applications with F# and Prism 5 (part 1)

 

F#

The designer tools of the Visual Studio should support F#. This would be critical for the adoption of F#. This is what I thought.

Last year I joined a large and complex project. The created system consists of several loosely coupled C# applications. Some components, the clients, have a WPF user interface. Some of the requirements are: easy to configure, extensibility and customization of the clients. It was decided to use modern design patterns like MVVM. It was also decided to create a composite clients. We could have created our own framework, but we decided to use one of the available frameworks: Prism 4.2 of Microsoft.

I learned that Visual Studio UI-designers are not essential for most of the development work. Blend is used by our UI-designers and the number of lines of code in the code behind code files is nearly zero. When implementing modern patterns like MV* (MVVM, MVC, MVP, etc. ), the tools should be great at designing UI’s and should have minimal integration with the code behind.

This leads to the conclusion that designer support of F# in not essential.

Prism 5

This year Prism 5 is released. As an experiment I want to build a real world application that consists of: Prism 5, XAML and F# an no C# code.

This application consists of 5 projects:

  • The shell, this is the container of the application.
  • Infrastructure, this is used to contain shared functionality between projects. It contains:
    • Base classes
    • Interfaces
    • Internal services
  • and 3 modules:
    • The Hello World module says hello to the world
    • The Prime module to determine the primeness of input
    • The message display module to show the results of the other modules

Components in Prism are called modules and these modules are not related to F# modules.

The final result looks like this:

image

Of course this is not really a real world application. It does not contain any really useful functionality and in a real world situations you could choose other ways to solve issues. I just kept it simple. I want to show how you could use Prism 5, its features together with F#.

Prism is a framework created by Microsoft patterns & practices. Prism is composite application framework. One of advantages of using a composite application is the reduction of complexity by separating concerns into functional components. Complexity increases linear with the number of components instead of quadratic and problems can simply be isolated. The details of Prism can be found at: Developer's Guide to Microsoft Prism Library 5.0 for WPF.

Prism 5 was released in April 2014. It replaces version 4.2. In the new release:

  • The dependency on Silverlight were removed.
  • Prism was modularized
  • Contains portable projects
  • Some components were replaced (the event aggregator) or a new (the ViewModelLocationProvider)

All the details of the changes can be found at: What's New in Prism Library 5.0 for WPF.

 

Hello World

In this first post I will port the Hello World from the Quickstarts of Prism to F#.

17: Getting Started Using the Prism Library 5.0 for WPF Hands-on Lab.

image

I will explain some details of the operation of Prism. Prism does contain a very detailed documentation, the missing details can be found over there. I will also discuss some F# language features. Prism is a framework created in c#, so we have to look at .NET classes, interfaces and inheritance.

Starting point of our port is the F# empty windows app (WPF) template. This template is created by Daniel Mohl:

F# Empty Windows App (WPF)

image

This template will enable us to use the XAML Type provider and it provides us the required assembly references.

After installing the VS template we use it to create a solution and a new project.

image

This project will contain the shell. The shell is the main component in a Prism solution. The shell does what it promises to do: it is a container and like most containers it is a simple box. It should have no knowledge of the content so the aim is to keep the shell as agnostic of its content as possible.

The Prism shell consists of three parts:

  • The view, this is UI part of the shell and is defined is XAML.
  • The app, this is the WPF functionality to manage the life time of the application.
  • The bootstrapper, this is the prism functionality to create the infrastructure so the modules can live.

clip_image002[5]

Then we install the required Prism libraries from nugget:

clip_image004

Prism requires the use of dependency injection framework. It does not implements its own framework, provides two integrations:

Both are provided by Microsoft.

It is possible to build an integration with your own favorite dependency injection framework.

The Hello World Quickstart uses Unity.

Next we remove the ApplicationResources.xaml file and rename the MainWindow.xaml file to Shell.xaml.

The Shell.xaml is a simple:

<Window 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.codeplex.com/prism"
Title="Hello World" Height="300" Width="300">
<
ItemsControl Name="MainRegion" cal:RegionManager.RegionName="MainRegion" />
</
Window>

The XAML contains two Prism element:


  • The prism name space
  • A placeholder so modules can present their content and functionality.

Next we delete all content of the app file.


First we add the module name and the name spaces:

module MainApp

open System
open System.Windows
open System.Windows.Controls
open Microsoft.Practices.Prism.Modularity
open Microsoft.Practices.Prism.UnityExtensions
open FSharpx

Next we make the Shell view available as a type:

type Shell = XAML<"Shell.xaml">

Now we need to create two classes that reference each other. In F# we have to define these classes at the same time by adding and:

App inherits from the Application class, the WPF class manages the lifetime of the application and provide the WPF functionality.

In the derived class we override the OnStartUp method and create a new instance of the Bootstrapper and call the Run method.

The bootstrapper derives from the UnityBootstrapper which derives from the abstract Bootstrapper both part of Prism. The abstract Bootstrappper provides the generic functionality from Prism, the UnityBootstrapper implements the Unity specific functionality.

The bootstrapper has serveral methods that can be overwritten to adjust the bootstrapper. We start with overwriting CreateShell method and InitializeShell;

type App() =
inherit Application()
override x.OnStartup(e) =
base.OnStartup(e)
let bootstrapper = new DemoBootstrapper()
bootstrapper.Run()

and DemoBootstrapper() =
inherit UnityBootstrapper()
override x.CreateShell() =
let window = Shell()
window.Root :> DependencyObject
override x.InitializeShell() =
base.InitializeShell()
App.Current.MainWindow <- x.Shell :?> Window
App.Current.MainWindow.Show()
override x.ConfigureModuleCatalog() =
base.ConfigureModuleCatalog()
let moduleCatalog = x.ModuleCatalog :?> ModuleCatalog
moduleCatalog.AddModule(typeof<HelloWorldModule.HelloWorldModule>) |> ignore

We end with the start method:

[<STAThread>]
(
new App()).Run()|> ignore

When we compile and run the application an empty shell is presented.

image

Next task: create a module that can be shown in the shell.

Create a new project of type F# Empty Windows App (WPF) and change the project output type to class library. One could start with a library project, in that case all required references have to be added by hand. Call the project HelloWorldModule.

Again we remove the ApplicationResources.xaml file and rename the MainWindow.xaml file. In this case to HelloWorldView.xaml.

Next we add references to the Prism dlls.

image

Again the XAML is very simple:

<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<
Grid>
<
TextBlock Text="Hello World" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Calibri" FontSize="24" FontWeight="Bold"></TextBlock>
</
Grid>
</
UserControl>

It will show the green text: “Hello World”.

Again we remove all code from the App file. We add the name space and open the required namespaces

namespace HelloWorldModule

open System
open System.Windows
open System.Windows.Controls

open Microsoft.Practices.Prism.Modularity
open Microsoft.Practices.Prism.Regions
open Microsoft.Practices.Prism.Mvvm

open FSharpx

Next we add the XAML of the view as a type.

type HelloWorldXaml = XAML<"HelloWorldView.xaml">

Now we create the main object of the module: the HelloWordModuel. This is a class that implements the IModule interface of Prism. The interface has one method, Initialize. This method does all the work to make the module available.

In our case we need register the view and add it to the “MainRegion”. This is the region we defined in the shell. To be able to register the view, the module needs access the RegionViewRegistry. It gets this by receiving a reference from the constructor.

type HelloWorldModule(registry:IRegionViewRegistry) =
let mutable regionViewRegistry = registry
interface IModule with
member
x.Initialize() = regionViewRegistry.RegisterViewWithRegion("MainRegion", fun _ -> HelloWorldXaml().Root:> obj)

The module is ready for use. The last activity is to tell the shell the module exits. This can be done is several ways. The Hello World implementation takes to easy way by adapting the bootstrapper. We add an extra override to the boostrapper. This time we override ConfigureModuleCatalog by adding our HelloWorldModule. The module catalog contains all available modules.

type App() =
inherit Application()
override x.OnStartup(e) =
base.OnStartup(e)
let bootstrapper = new DemoBootstrapper()
bootstrapper.Run()

and DemoBootstrapper() =
inherit UnityBootstrapper()
override x.CreateShell() =
let window = Shell()
window.Root :> DependencyObject
override x.InitializeShell() =
base.InitializeShell()
App.Current.MainWindow <- x.Shell :?> Window
App.Current.MainWindow.Show()
override x.ConfigureModuleCatalog() =
base
.ConfigureModuleCatalog()
let
moduleCatalog = x.ModuleCatalog :?> ModuleCatalog
moduleCatalog.AddModule(typeof<HelloWorldModule.HelloWorldModule>) |>
ignore

When we compile and run the application the Hello World Module is present, the green Hello World Text is shown.


clip_image002[7]


This completes the port of the Hello World from the Quickstarts of Prism 5 to F#.

There is at least one problem with the Hello World implementation. The Shell has to have knowledge of the HelloWorldModule.

Next time we have a look how we can fix this. There are other ways to configure Module Catalog. If we use Managed Extensibility Framework (MEF) as the dependency injection framework we can even can reduce the configuration to copying module dlls into directory.


Updates:



  • I got a tweet from Reed Copsey (@ReedCopsey). He suggested to use FsXaml. By getting a version via Nuget:
  • image

replace the namespaces:


    open FsXaml

and adjusting the creation of the view:

and DemoBootstrapper() =
inherit UnityBootstrapper()
override x.CreateShell() =
Shell().CreateRoot() :> DependencyObject
override x.InitializeShell() =
base.InitializeShell()
App.Current.MainWindow <- x.Shell :?> Window
App.Current.MainWindow.Show()
override x.ConfigureModuleCatalog() =
base.ConfigureModuleCatalog()
let moduleCatalog = x.ModuleCatalog :?> ModuleCatalog
moduleCatalog.AddModule(typeof<HelloWorldModule.HelloWorldModule>) |> ignore

it works again.


No comments:

Total Pageviews