DEV Community

Matt Eland
Matt Eland Subscriber

Posted on • Originally published at killalldefects.com on

WPF Core with F# Libraries

A static image of the Squirrel application in WPF Core.

In this article, we’ll take an F# class library and build a graphical user interface for it. This library was previously designed to be called from an F# class library. Since we can’t currently use F# in a WPF application out of the box, we’re going to explore how C# can work with F# code.

This article stands on its own, but is also part 4 in an ongoing series on building a genetic algorithm in F#. Previous entries are listed below:

  1. Creating a F# Console Application
  2. F# Squirrel Brains: Adding Actors and Getting Functional
  3. F# Unit Testing – Refining the Squirrel Simulation

Learning Objectives

In this article we’ll cover:

  • Creating a WPF Core application in .NET 3.0
  • Calling F# code from C# code

By the end of the article we’ll have a working WPF app that provides a graphical version of the console application from previous entries.

What is WPF and how does it work?

WPF was created as the successor to Windows Forms. At its center is XAML or eXtensible Application Markup Language. XAML is UI markup that binds to an underlying view model. This powerful language gives WPF its flexibility and power.

The view model is what ties the UI (or view) to the application’s logic and the logic to the data (or model). This follows the Model / View / ViewModel pattern, or MVVM for short.

MVVM Interaction Pattern
Model / View / ViewModel (MVVM) Interaction Pattern

In MVVM, views are responsible for the static presentation of data and binding to the view model. The view model is responsible for executing commands invoked by the view and adapting the model into a format that is easy to use for the view. The view model is also responsible for telling the view when data changes via property changed notifications.

While WPF has now been around for awhile, it only recently became part of .NET Core with the release of .NET Core 3.

Creating the WPF Application

This article assumes that you’re starting from the article4start tag on my GitHub repository, but it also works as a loose guide if you have an existing solution with an F# class library.

Add a new project window with WPF App (.NET Core) selected.

Click Next, then name your project and click Create.

Once your project is created, we’ll need to set the WPF Core app as our startup project by right clicking on it and selecting Set as StartUp Project as pictured below.

Project context menu in Solution Explorer with Set as Startup Project highlighted.

This will launch the application when you click Debug or Start without Debugging (my preferred way).

Go ahead and start the application now and you should see an empty application.

Referencing the F# Library

Once your project is created, right click on it in the solution explorer and select Add Reference...

Project context menu for its dependencies node with Add Reference... highlighted.

Next, select your F# library project and click OK.

Reference Manager with a reference added to the logic assembly.

We’ll now be able to reference F# functions from C# classes in our WPF Core app.

Hello Squirrel

Next we’re going to make our first view model and wire up the user interface to display a text-based grid. This grid consists of objects defined at the simulation layer in the F# class library.

In a moment we’ll embrace WPF and make this look nice, but for the time being let’s just display some text on the screen to demonstrate that the connection is working.

Building the ViewModel

Create a new class called MainViewModel and paste in the following code:

This is a fairly simple ViewModel that doesn’t even have property changed notifications. It creates a new World instance in its constructor.

This is an F# type named World defined inside of the MattEland.FSharpGeneticAlgorithm.Logic.World module. This odd World.World syntax is a strong reason to not name a type the same name as a module or namespace.

At line 15 we declare a public get property named TextGrid. This property is what we’ll bind to in the user interface. Because complex logic should be done in methods and not property getters, I call BuildAsciiGrid from the property getter. Technically speaking, I shouldn’t even be doing that type of method invoke here, but we’ll change the implementation in a bit to something more standard.

BuildAsciiGrid is able to work with properties on the World type as if they were on any other object, and is able to call out to functions in the World module by name.

Next, we’ll go into the MainWindow.xaml.cs file and modify its constructor to set its DataContext to the ViewModel like so:

DataContext is essentially the binding context inside of the XAML file. While you can change an element’s DataContext, by default it will inherit the parent’s DataContext and so, we’re going to set the entire Window’s DataContext to our object.

Binding the UI in XAML

Finally, let’s modify MainWindow.xaml to update the UI to make use of the new context:

This XAML markup is a flavor of XML that WPF uses to construct the user interface. It is intended to be a designer and tool accessible file, much like a HTML document for the web.

At the top you notice a number of xmlns declarations to define various XML Namespaces. The one to highlight here is xmlns:local on line 6 as this is a line I added in order to refer to our view model by class later on in line 12.

Line 12 declares the design time DataContext for the Window, which helps with code completion. Note that the d: prefix references the blend/2008 namespace definition on line 4.

Line 14 is really what does everything for us. We have a TextBlock that displays some text on the screen. We set some basic font properties for display purposes, and then on line 16 we bind the TextBlock’s Text to the ViewModel’s TextGrid property.

Now when we run the application we get a static view of the simulation:

WPF Core app showing an ASCII grid

Connecting the WPF Core App to our Logic

Okay, so we have a weird-looking grid layout. So what? In our console application we could at least control the squirrel.

Let’s add some UI controls and start making this a bit more polished.

We’re going to start simply by adding a Restart button.

NotifyPropertyChanged Support

In order to support changes to the UI, we’re going to need to raise property changed notifications. In order to do this, let’s add a new PropertyChangedBase class:

WPF looks for implementations of INotifyPropertyChanged and subscribes to PropertyChanged event invocations in order to update the user interface. It expects either the name of the property on the ViewModel that changed, or an empty string to indicate that all properties have changed.

We’ll see how we invoke this in a moment, after we introduce our command object.

ActionCommands

In WPF you can handle click events manually, but it’s considered somewhat of a bad design practice. It’s much more standard to create commands that can be bound directly to the user interface. This way commands can be shared between related controls and the view layer is not responsible for things like click handlers.

In order to support commands, let’s make a minimal ICommand implementation that takes in an Action and invokes it when executed. This command will be flexible and reusable and allow our view model to define a wide variety of commands without needing a lot of classes implementing ICommand.

Here’s our simple command:

The most critical bits of this class definition are lines 5 where we accept in an Action into the constructor and store it, and line 12 where we invoke the Action when the command is executed.

Note that this command is set to always return true for CanExecute. This is because we don’t need to disable buttons in this application. If you needed to disable and re-enable buttons, you could implement CanExecute and then raise CanExecuteChanged when the command should be re-evaluated.

Putting it All Together

Now lets see how these two techniques come together to serve up a responsive UI. We’ll start at with the XAML.

This is a bit more complex now. Note that we’re defining a style we can apply to buttons and using more complex layout controls like DockPanel and StackPanel. This article won’t cover how those panels function, as UI design isn’t our focus here, but know that these are panels that have different layout strategies for their child elements.

Notice that in line 30 we’re now binding the button’s Command to a property on our ViewModel called ResetCommand. We’ll see what that is in a minute.

Okay, now that we’ve seen the view changes, let’s take a look at the view model changes:

First of all, note that we now inherit from NotifyPropertyChangedBase so we can fire off property changed notifications.

Secondly, instead of relying on a World instance, we now hold a GameState instance, which is what the F# library uses internally to hold the full session state.

Third, notice the ResetCommand. We define that as an ActionCommand and the Action it takes in its constructor is the Reset() method. This is a shortcut for the full syntax of () => Reset().

When Reset() is invoked it talks to F# to construct a new GameState instance with a new World inside of it. This State is then pumped into the State property setter.

The property setter for State is interesting in that it fires off notifications for a few different properties. This means that when the state changes, WPF Core will re-evaluate anything bound on those properties, making UI refreshes efficient.

When you put it all together, you get something that updates the UI with a new layout when reset is clicked:

Cleaning up F# Enums

Before we go on, I want to point out an oddity I noticed in working with a F# type.

I had defined the following faux-enum in F#:

type SimulationState = Simulating | Won | Lost

Seems pretty simple. SimulationState is either going to be Simulating, Won, or Lost. While technically an F# discriminated union, this is effectively an enum. At least that’s what I thought.

I was wrong. When I tried to switch on the SimState property of State, C# would not let me match on its values because it didn’t think that Won was constant, for example.

If I instead define type SimulationState = Simulating = 0 | Won = 1 | Lost = 2, I can use it in a normal switch case or switch expression like follows:

The downside here is that my F# code now always has to qualify enum values with their type name. For example, any instance of Lost before in F# code now appears as SimulationState.Lost.

Adding Movement

Now that we’ve gotten the basics of WPF UI, let’s add some more commands around player movement.

In order to support 9 similar movement commands (including wait) without adding a lot more code to the view model, let’s take advantage of CommandParameters in WPF Core. These parameters are passed into the ICommand instance and can modify how commands behave.

Let’s take a look at how these are used from XAML:

Here our 3×3 grid of movement commands all take in a command parameter that is different, but all point to the same movement command.

MoveCommand interprets the parameter and switches on possible values, then maps each to a corresponding value to send on to the F# class library:

This is largely nothing new, though we have a new direction parameter. Note that with some of this syntax such as ??= and the switch expressions, I am using C# 8 language features.

In order to support the direction property, we now need to be able to specify that an ActionCommand either works with an Action or an Action<T> – that is to say, a version that doesn’t need parameters and a version that does. This is accomplished by adding a new constructor to ActionCommand as follows:

And now, when we run everything, we get some semblance of our console application inside of WPF (The player controls the S):

Dressing up a Squirrel

Okay, so we’re now at functional parity with where we were before, but the game still looks ugly. Let’s clean it up slightly for entirely cosmetic purposes.

I’m going to start by finding a number of images for actors in the game world as well as a background image for grass. I’ll then paste them into the WPF Core project directory.

The images should show up in Visual Studio and you will then be able to click on them in solution explorer and view their properties. Make sure each image has its Build Action property set to Resource as pictured below. This will allow WPF to embed it into the application at runtime.

Setting the properties of an image to Resource in Visual Studio 2019.
Set Build Action in the Properties pane. It is not necessary to open the image in the editor.

Squirrel Views

Next let’s look at how these images will be rendered on the screen. We’ll replace the grid containing the ASCII text with the following XAML:

First of all, we start with an image background bound to the grass texture. This will help with overall theming.

Secondly, we introduce a ViewBox hosting a Border hosting an ItemsControl. We’ll talk about the former to in a bit, so let’s focus on that ItemsControl.

The ItemsControl is a built-in control for rendering a collection of objects. In our case, we want to render images for each actor, and we want each actor to appear in the appropriate spot.

In order to do this, we have the ItemsControl use a Canvas for its layout, starting at line 13, instead of the built-in vertical StackPanel.

Next, we need to tell each ContentPresenter that the ItemsControl builds where to position itself on the canvas in order to present the content appropriately. We do this by styling the ContentPresenter and binding the Canvas.Left and Canvas.Top attached properties to properties on the ViewModel we’re about to create.

Finally, we customize the presentation of the item by providing a custom ItemContainerTemplate and specifying an image with a border around it. The image’s content is bound to a property on the view model we’ll be creating in a moment.

Finally, to back up a moment, the viewbox and border at the top level help us see the boundaries of the world appropriately while letting the game world scale up and down as the application resizes, while not stretching abnormally.

Actor ViewModels

We’ve talked about the view model for the individual actor. Let’s look a bit more at that now:

There’s not a lot in here that’s new.

Note that we’re computing the canvas position of the item by multiplying by a size of 10 pixels per item. This is consistent with the overall size of the canvas. The ViewBox actually scales the images up larger than this, so what’s really important is the proportion to the full ItemsControl width of 130. Since we have a 13×13 grid, 10 is appropriate.

The bulk of this class is spent in the ImagePath getter. Here, ActorKind is a discriminated union and so we can’t do the same sort of switch expressions we can on actual enums. Since we’re relying on the Squirrel’s hasAcorn property, we can’t convert ActorKind to an enum.

Another interesting note here is how F# requires you use NewSquirrel() with the value passed in on whether or not the squirrel should have an acorn. Again, this is due to defining Squirrel as Squirrel of hasAcorn:bool in our F# ActorKind definition.

A final note on this view model is that it has no property changed notifications since it works with a single piece of immutable state. My suspicion is that with F#’s preference for immutability, this may be a common practice when working with F# models.

Bringing Actors Together

So how are the ActorViewModel instances handled?

The UI is bound to an object called an ObservableCollection. This is a fancy type of collection that fires collection changed notifications so that WPF Core knows that items were added or removed.

With an ObservableCollection, typically you bind the UI to the same collection and then modify the collection at will. This is what we’re doing here.

When the State changes, we clear out the collection of Actors and then add in new ActorViewModel instances for each active (non-dead) actor inside the game world.

End Result

Ultimately, we were able to take our console application and port it over to WPF Core fairly easily and add visual polish in the process.

An animated GIF showing the game in action.

The finished result from this article is on GitHub in the article4 branch if you want to play more with the application or see how it works in more detail.

Ultimately, F# and C# can talk together fairly easily, though there are a few minor bumps and hurdles to overcome.

It’s also remarkably easy to get started in WPF Core, and XAML remains a very strong language for application development.

What’s Next?

The squirrel application is not yet complete. While the core simulation logic and WPF Core UI are ready, we haven’t even started playing with artificial intelligence.

Next up, we’re going to start exploring genetic algorithms and fitness functions and see how they can be applied to teach the computer how to train a squirrel brain to win the game as easily as a human player can.

Stay tuned.

The post WPF Core with F# Libraries appeared first on Kill All Defects.

Top comments (0)