Learn about my newfound love for the ReactiveUI MVVM framework.
My day job is to wrangle 12 million lines of 20+ year-old C and C++ using a custom Win32-based UI library that was built in the mid-90s and never fundamentally improved. It does what it was designed to do really well (bind transactions to views, zoom between list items, the transactions they compose, and the reports on those transactions) but sometimes I idly wonder what’s been going on in bleeding edge Windows app development in the interim.
Last week I downloaded and played around with GitHub’s excellent Windows client. I vaguely remembered a blog post on the gear underlying the desktop app awhile back, and I was interested to see where it was at these days. You know you’re a geek when you read the entire list of licenses in the About view to get a sense of the underlying technology.
After looking around at the various .NET libraries involved, and reading some follow-up blog posts I decided to build a front-end to a developer tool I whipped up for our developers and QA engineers at work.
Essentially, in debug mode QuickBooks Payroll can talk to a variety of backend environments. Configuring the various endpoints involves editing several config files that are in different places depending on whether you have an installed build or a developer build. To make it easier to configure things I wrote a command-line tool that can tell you what environments you’re currently configured to talk to, as well as list available environments and change the current environment. I built the command-line tool on top of a library that implemented all the core logic because I knew that I’d eventually want to build an easier-to-use GUI on top of it.
So as a little weekend project I took inspiration from GitHub for Windows Metro/Modern visual design and a desire to look further into ReactiveUI’s take on multi-threaded UI.
Multi-threaded UI development typically has one sticking point; it’s easy enough to define a lambda or function and set it up to run on a separate thread but one has to be very careful when moving the result of that lambda back onto the UI thread to display it. Reactive’s secret sauce is to simplify this dangerous activity and deliver a true ‘fire-and-forget’ multi-thread solution.
Thus far I’ve found that it’s very easy to chain async commands AND provide a nice responsive user experience. In cases where you have certain application behaviors that are gated upon other actions completing the ReactiveUI framework makes it extremely simple.
An example:
- App bootstraps.
- Check for environment definition updates.
- If updates are available; download and install updates.
- When updates are complete, or if no updates are available then validate current saved environment settings.
- If settings are invalid or missing show the special UI telling user to edit the app settings.
- If settings are valid then discover the current environment.
- Once the current environment has been determined; set the properties on the view model that are bound to the UI that describe the current environment.
The state machine representing the above workflow was implemented with three View Models bound to one XAML MainWindow. The cleanest one is below, and I’ve commented how the commands to check for updates and download and install updates are implemented (I’m still working on tightening up the other two).
I’ve included the entire file below because I think it’s valuable to view the initialization of the commands and observers and their targets in context. This view model encapsulates all of the updater functionality and how the different states of updating are exposed to the UI.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
|
The data that the updater object fetches from is located on an intranet share, and so it was important to check for and apply updates asynchronously (especially if, like me, you’re working over the VPN).
I’m unsure at this point if it’s ok that I’m updating the UpdateState
property from within many of the lambdas. I feel that there’s something risky going on there but everything seems to work fine for me as it is.
I’ll break down the two main uses of Reactive’s async commands below. Note that I’m using the UpdateState
property primarily outside of this class; the window binds certain elements’ visibility to the state of the updater object, but only when the state has certain values.
1 2 3 4 5 6 7 8 9 10 11 |
|
When the async function returns the new EnvironmentsFileUpdater
instance and sets the backing field of the Updater
property, I then lean on an Observable. Below you can see that I set it up such that when the Updater
field changes we check whether an update is available and then either kick off the DownloadUpdates
command or set the overall state to UpdateState.Completed
to signal to the external listeners that the update process is complete.
The Status
property is the string value that’s bound to the UI that updates the user as to what’s happening in the update process.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
The DownloadUpdates
command actually handles the work of downloading the latest environment definitions and installing them to the appropriate local storage mechanism. Again, it must reach out over the intranet and so it’s best to make this action asynchronous. Most of the code you see below is concerned with updating the UI-bound status value; line 5 is the key method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
I hope this has been a decent non-trivial example on how to use the ReactiveUI framework to build WPF view models; if it looks interesting have a look at the great docs that have been synthesized from Paul Betts blog post examples of different usages of the framework.