United Kingdom: +44 (0)208 088 8978

Elmish.WPF

In this week's blog post, Matt takes a look at Elmish.WPF, a library for writing WPF apps using MVU.

We're hiring Software Developers

Click here to find out more

Do you write desktop apps? Do you love MVU? Elmish.WPF has you covered.

What Elmish.WPF is

According to Elmish.WPF's README,

Elmish.WPF is a production-ready library that allows you to write WPF apps with the robust, simple, well-known, and battle-tested MVU architecture, while still allowing you to use all your XAML knowledge and tooling to create UIs.

You write model and update code as you would for a regular Elmish application, but the view works differently. Rather than having a function that takes your model and dispatch function as input and gives a UI specification as output, in Elmish.WPF you instead have a function that sets up bindings to views defined in XAML.

Project structure

The XAML development experience is better with C# than F#. As a result, a typical Elmish.WPF project has a C# project for XAML and F# projects for everything else (model definitions, update code, bindings etc.). The standard way of doing things is to have a single F# project that the C# project refers to, but the library can handle alternative project structures.

Bindings

It's useful to know a little about how data binding in WPF works. Briefly, you have a target UI element with properties, which interact with properties on a binding source.

To better understand how bindings work, we'll take a look at the SingleCounter sample provided in the Elmish.WPF repo. Check out both the SingleCounter.Core project, which contains the core F# code, and the SingleCounter project, which contains the XAML.

Target Elements in the XAML project declare bindings from one of their properties to a property on the binding source:

    <TextBlock Text="{Binding CounterValue, StringFormat='Counter value: {0}'}" Width="110" Margin="0,5,10,5" />
    <Button Command="{Binding Decrement}" Content="-" Margin="0,5,10,5" Width="30" />
    <Button Command="{Binding Increment}" Content="+" Margin="0,5,10,5" Width="30" />
    <TextBlock Text="{Binding StepSize, StringFormat='Step size: {0}'}" Width="70" Margin="0,5,10,5" />
    <Slider Value="{Binding StepSize}" TickFrequency="1" Maximum="10" Minimum="1" IsSnapToTickEnabled="True" Width="100" Margin="0,5,10,5" />
    <Button Command="{Binding Reset}" Content="Reset" Margin="0,5,10,5" Width="50" />

For example, the Text property on the first TextBlock element is bound to the CounterValue property on the binding source. The binding source is created in the bindings function in the core project:

let bindings () : Binding<Model, Msg> list = [
  "CounterValue" |> Binding.oneWay (fun m -> m.Count)
  "Increment" |> Binding.cmd Increment
  "Decrement" |> Binding.cmd Decrement
  "StepSize" |> Binding.twoWay(
    (fun m -> float m.StepSize),
    int >> SetStepSize)
  "Reset" |> Binding.cmdIf(Reset, canReset)
]

As you can see, there are multiple binding types available. Binding.oneWay is for read-only properties, while Binding.twoWay allows for properties that can be read from and written to. The Elmish.WPF tutorial has a section explaining the different binding types.

The rest of the F# code is standard Elmish:

type Model =
  { Count: int
    StepSize: int }

type Msg =
  | Increment
  | Decrement
  | SetStepSize of int
  | Reset

let init =
  { Count = 0
    StepSize = 1 }

let update msg m =
  match msg with
  | Increment -> { m with Count = m.Count + m.StepSize }
  | Decrement -> { m with Count = m.Count - m.StepSize }
  | SetStepSize x -> { m with StepSize = x }
  | Reset -> init

Conclusion

Elmish.WPF is a great tool. It allows XAML developers to benefit from F# and MVU, and F# Elmish developers to use their skills to make desktop applications. Thanks to the contributors for providing another wonderful library to the F# community!

If you want to learn more about Elmish.WPF, check out the following resources: