Let’s talk about Elmish.WPF and how it brings Model View Update (MVU) architecture and Functional Programming to desktop .NET development.
In this article I’ll share the results of my early journey in learning Elmish.WPF and walk you through setting up a new project. Don’t know what Elmish.WPF is? That’s fine — I barely knew myself. I’ll teach you everything you need to know.
My main goal for you in reading this is that you:
- Learn what Elmish.WPF is
- Understand how to get started with Elmish.WPF
- Think about the advantages F# and Model View Update (MVU) application architectures bring to the table
Along the way, we’ll cover:
- What is Elm anyway?
- What Elmish.WPF is and how it helps with XAML applications
- The differences between MVVM and MVU application architectures
- Installing and referencing Elmish.WPF
- Hosting WPF content from an F# library
- Creating a XAML control library to represent static views
If you’re a .NET developer I encourage you to give this a read, even if you aren’t extremely interested in desktop or mobile development. Elmish style architectures are spreading and understanding Model View Update at a high level will help you down the road.
If you haven’t worked with F# or WPF, you should be fine to read the article, but if you’ve never worked with Visual Studio or .NET technologies before, this is likely not the article to start with.
Elmish? What is this? Middle Earth?
Recently I’ve been seeing the term “Elmish” floating around a lot more. Part of this is my interest in F# and other functional programming languages, but I also see the term come up more often in my existing circles.
This is supported by the following Google trend results over the past 5 years:
While it’s not a huge spike, there’s clearly an increased frequency in interest in the topic over the last few years — primarily when paired with searches on F#, React, or Fable.
So what is Elmish?
Elmish languages are functional programming languages that also follow the Model View Update (MVU) pattern which has the following basic components:
- Relies on an immutable model
- Populates a view based on the current model
- Sends messages on events in the user interface
- Updates the effective model by feeding the old model and the messages into a Reducer
Why is this attractive?
Well, for one, the use of immutable state and reducers prevents a number of quality issues.
For another, functional programming is a fantastic way of improving software quality. See my prior article on why .NET managers should strongly consider F# for more information on my views on functional programming.
Essentially, following a MVU architecture is going to be weird and different and may slow some development efforts up front, but will simplify and improve your maintenance work in the long term. This happens via functional programming’s tendency to eliminate entire classes of defects and reducer-based architectures making bugs easy to isolate and resolve.
An Introduction to Elmish.WPF
Now that we talked a bit more about Elmish languages and the idea of MVU, let’s introduce Elmish.WPF.
Elmish.WPF is a library that allows you to build Windows Presentation Foundation (WPF) desktop applications using Elmish principles.
Typically in WPF, we follow Model View ViewModel (MVVM) architecture as pictured below:
Elmish.WPF lets us work with immutable models that are swapped out based on user actions.
Because we’re working with immutable state, the need for property changed notifications via the ViewModel is reduced or eliminated and so MVVM makes less sense than a MVU-based architecture like Elmish.WPF’s.
Elmish lets you run a core MVU logic that eliminates the need to create ViewModel classes while still allowing you to build powerful user interfaces with XAML like you’re used to.
Let’s take a look at how this works and how to get started with Elmish.WPF.
Getting Started with Elmish.WPF
Creating the Projects
First, you’ll need to create an F# .NET Core Console Application (Elmish.WPF also works for .NET Framework, but I’ll only be covering .NET Core in this article).
We’ll do this in Visual Studio 2019 Community Edition for the purposes of this article.
Don’t worry about the use of the console application in the project type. We’ll be modifying the properties of this project to customize it. This project will be the main entry point of our application and hold the main Elmish processing loop.
Next, we’ll add a C# WPF Custom Control Library (.NET Core) project to the solution:
This C# project is going to hold our XAML views. We need to use C# for this because F# does not support definitions of XAML files yet — at least in Visual Studio.
Next, add a reference from your F# project to your C# library so that we can reference the main application views.
Before we can add Elmish.WPF to the mix, we should define our model and view.
Let’s do that next.
Creating the View
In the WPF Control Library project, add a new Window called
We’re just going to be doing some very simple things in this example. Specifically, we want to have a button for the user to click and display the number of times the button has been clicked.
Not the most Earth-shattering code, but enough for a simple demo application.
The XAML for this is pretty simple.
Inside the generated XAML file, replace the
<grid></grid> code with the following markup:
Note that I’m not including the full XAML file as your namespace is going to be different than mine and we don’t actually need to import any new namespaces for the above code to work.
Creating the Data Model
Next let’s write some code to handle storing the application state.
In our F# project, add the following definition to the top of the
This is an incredibly simple model, but we really don’t need a lot for our example.
Note that the names in the Model type do not need to be the same things you’re binding to in the view. The Model type isn’t what the view uses as its
DataContext and, because of that, we can name our properties whatever we want.
Now that we have our model and view in place, let’s add Elmish.WPF to the scene to tie everything together.
Manage NuGet Dependencies for your F# project and add a reference to
Elmish.WPF as pictured below:
Now, we can start modifying our project to work with Elmish.WPF.
Adding WPF Support to the F# Project
This is the least fun aspect of this, but I’m sorry — we’re going to have to edit a project file.
We’ll need to edit our F# project’s
.fsproj file and add the
UseWPF node to its
PropertyGroup element as shown below:
Note that I’ve included my full
.fsproj file contents for reference, but you'll only need to add that one
UseWPF node pictured above.
Save that and close the file.
Next, we’ll need to tweak some settings in Visual Studio to tell Visual Studio to launch our application as a Windows Application, not a console application.
Go into project settings for your F# application and change the startup mode to Windows Application as pictured below:
This step is technically optional, but if you don’t do this you’ll later see both your application window and a console window open up, which is not what you want to see.
Building the Elmish.WPF Processing Loop
Phew. All that work to set up our projects was unfortunate, but thankfully that’s one-time work and most of the hoops we jumped through were because F# does not yet support XAML content.
Now we can actually focus on what we came here for — learning Elmish.WPF!
We’ll be working entirely in
Program.fs for this section.
First, add an
open Elmish.WPF statement to the top of the file so that we can work with the main Elmish.WPF framework.
While you’re at it, you’ll need to add an
open statement for the same namespace that your
MainWindow was defined in. Check the
MainWindow.xaml.cs file for that namespace as it will be different based on what you named your project.
Remove the existing default program code at the bottom of your
program.fs file. You're not running a console application and so you won't need it. Instead, add the following:
So, this won’t compile at the moment, and there’s a lot going on here and we should talk about that.
First, on line 2, we define attributes telling F# that this is the application entry point and it should run in an appropriate threading model.
Next, we invoke the Elmish.WPF extension method of
mkSimpleWpf to create a standard Elmish.WPF application.
This function needs three arguments:
initfunction to generate the application's starting state
updatefunction to act as a reducer to generate a new state based on a message
bindingsfunction to provide information to the auto-generated
DataContextthe View will be bound to.
We’ll go over each one of these functions in the next section.
Finally, we execute
runWindow and specify a new
Window instance for the application to display. This instance we provide is the
MainWindow we defined in our WPF Control Library project earlier.
Implementing the Core Functions
Let’s look at how to implement the
init (Starting State)
The easiest of the three, the
init function just returns an instance of
Model in its starting state.
Ours looks like this:
Note that I’ve included the
Model we defined earlier in the above snippet for reference.
update (The Reducer)
Update gets a bit trickier.
update relies on a message, we're going to have to create a Discriminated Union of the types of messages we expect. I'll call this type
MessageType and give it the options of a button click event or a reset event, even though our view doesn't actually have a button for the latter.
It looks like this:
MessageType is almost an enum here (it would be if we explicitly assigned values to things), but we're keeping it as a discriminated union as future message types may have state associated with them (this will be covered in a future article).
Now that we have our MessageType defined, we can implement the
update function takes in a
MessageType and the old
Model and outputs a new
Model. How you do that is up to you, but I strongly recommend using F#'s
match syntax as shown here:
Here we match over the various supported message types and return a new state based on the operation. In the case of
Reset, we just call
init again giving us our starting state.
In the case of
ButtonClicked, we use F#'s with syntax to create a clone of
model with only a minor difference: the
ClickCount property should have a value 1 higher than the prior model did.
That’s it. Just a simple reducer that performs simple operations.
If your operations get complex, I strongly recommend you pull them into their own functions and simple invoke those functions from the
update method. This is exactly what we're doing with the call to
init() on line 5.
bindings (DataContext Generation)
Finally, we get to the
binding function which configures an auto-generated data context object.
The syntax of this is a little odd, but we’re effectively just building a list of properties on an object and configuring how they behave.
Let’s jump into the code:
The one-way bindings on lines 4 and 5 are fairly simple. We use
Binding.oneWay to specify a function that returns a value from our model and exposing it as a property named the string at the beginning of the line.
For example, line 5 declares a new property named “Message” and grabs the value for it from the
The commands generate an
ICommand instance that WPF can bind many controls to. In our case, the button is will be bound to the
ClickCommand property and will invoke this command when executed.
Binding.cmd ButtonClicked syntax tells Elmish.WPF to:
- Create an ICommand binding
- Send a message to
MessageTypespecified as well as the current model.
- Re-evaluate bindings to update the view with the new model.
Tying it all Together
And that’s all there is to it.
Now when we run we’ll get a fantastic button that we can click to our heart’s content and watch the counter update itself in real time.
For reference, my complete
Program.fs is included below:
Next Steps & Closing Thoughts
Hopefully this adequately explains Elmish / MVU architectures and how to get started with Elmish.WPF as well as why you might want to.
I’m still early on in my evaluation of the technology and will be looking to follow up this article with larger-scale examples and practices on something other than a trivial starter application.
Because of this, I’m reserving judgement on recommending Elmish.WPF, but I wanted to share this first leg of the journey with others so that they could understand Elmish.WPF better and learn more about how it works and how they could get started with it.
For next steps, I strongly recommend reading the Elmish.WPF Tutorial online as it gets into much more complex binding scenarios and capabilities.
Originally published at https://killalldefects.com on December 30, 2019.