Home

Awesome

Elmish.WPF.Dynamic

A dynamic Elmish UI library for WPF

See also Elmish.WPF for a robust “half-elmish” solution with static XAML views.

What is this?

Elmish.WPF.Dynamic is a library that allows you to write dynamic WPF views using an Elmish architecture.

It is superficially similar to Fabulous in using a generator and config file. However, the view syntax is different, and under the hood, the “shadow DOM” is more strongly typed in Elmish.WPF.Dynamic.

How is it used?

Check out the TestApp project in this repo for a complete working app. Some familiarity with Elmish is assumed.

The view syntax is based on mutable properties. Note that this is just a syntax for creating shadow DOM elements; it has no impact on the immutability of your model.

For example, using normal property initialization syntax:

TextBlock(
  Text = model.MyText,
  IsHyphenationEnabled = true
)

There are also constructor overloads taking an initializer function. This provides better discoverability since you can “dot into” the types to discover the available properties. It also allows you to factor out common setters.

TextBlock(fun tb ->
  setCommonTextBlockProps tb
  tb.Text <- model.MyText
  tb.IsHyphenationEnabled <- true
)

You can even mix and match:

TextBlock((fun tb ->
  setCommonTextBlockProps tb
  tb.Text <- model.MyText),
  IsHyphenationEnabled = true
)

Or:

TextBlock(
  setCommonTextBlockProps,
  Text = model.MyText,
  IsHyphenationEnabled = true
)

The view function must either return a single Window (and the program started with Program.runWindow) or a list of Windows (and the program started with Program.runWindows).

Available types and props

As a general rule, every relevant WPF DependencyObject has a corresponding shadow DOM type (ultimately deriving from a based type currently called Node, like Fabulous’ ViewElement), and all relevant properties on each control have a corresponding property on the shadow DOM type. The shadow DOM type hierarchy mirrors the hierarchy of the WPF controls.

Attached props are defined as extension properties on the relevant types. For example, DockPanel.Dock is defined as an extension property DockPanel_Dock on UIElement.

In addition, the following special props exist:

Current project status

This is a fairly usable proof of concept. If the current limitations are not important to you, I see nothing wrong with creating apps with it. (There may of course be bugs, as always.)

Some important and helpful batteries are included, such as lazyWith for memoizing views, elmEq and refEq as sane defaults for lazyWith, getResource to interop with application resources, and a TextChangedEventArgs.Text extension property to easily retrieve the current text of a TextBox when changed. The generator should also work for 3rd party controls and attached props, though that is currently untested. However, there are at least a few limitations which may or may not be significant for you. They are described below.

Wanted: Someone to carry the project forward

I have no immediate plans to continue working on this, and I can’t promise I’ll get back to it in the future. Anyone is welcome to drive the project forward, whether by taking over this repo, creating a fork, creating your own project inspired by this, or submitting PRs. If you are interested in doing significant work, please take over the project instead of submitting PRs.

For me, this project is purely based on personal interest, and at the moment I’m more interested in creating apps with Fable.React and Electron due to the battle-tested nature of React and Electron and the synergy of React with an Elmish architecture. I am also thinking that perhaps static XAML views with Elmish.WPF synergizes better with WPF than dynamic views, since WPF is heavily based on bindings, templates, etc. While static views are far from as composable as dynamic views, Elmish.WPF puts all the power of WPF at your fingertips, while still allowing most of the goodness of an Elmish architecture.

Current limitations

In short, it seems that some WPF functionality that is desirable also in an Elmish architecture, such as virtualization and key bindings, depend on functionality that should generally not be available in an Elmish architecture, such as templates/bindings and ICommands. While solutions can be found, it’s certainly not trivial.

The most notable current limitations are:

TODOs

Primary challenges

Other challenges

Missing props/types/features

3rd party shadow DOM generation

General improvements

Cleanup of generated code

Optimizations

Config file format

Implementation details

This section is mostly relevant for anyone wanting to take over.

Note that due to the WIP/POC nature of this project, any file/module/type/variable names may or may not be well thought through or accurately reflect the current state of the items they describe. Most names should be OK, but please rename to your liking.

The solution contains three projects:

Some notes about the generated shadow DOM types:

Please ask if you have more questions about the code.