A variation on Matt Moloney's Undo/Redo "Memento" pattern

The state is held in an agent. This is not 100% needed, but makes for a nice example of using agents in this way.

The agent is encapsulated in an object.

The object can serve as a data context for a Xaml WPF window, reporting whether Undo/Redo is available.

The example shows how to do some WPF scripting with F# where the Xaml code is inline. This can be a good way to learn things like data binding. You could equally read the Xaml from a file created using the designer.

I aboslutely love this sample: it demonstrates yet another example of "oh so lovely" F# design-pattern coding by using something we're all very familiar with (undo/redo) and which we know we want our applications to have.

When I looked at Matt's code, I thought of making a couple of tweaks, which I thought I'd write up here:

First, I adjusted the code to "capture the synchonization context" in the Memento object, and to do this late (when a button is pressed), rather than early. This means that, from the outside, the Memento object is just like any other GUI object - no use of background multi-threading is exposed. This means we could replace the use of an agent to hold the background state if we wished. Further, we are sure to capture the right synchronization context, i.e. the one active when a WPF button is pressed. .

Second, I adjusted the WPF fragments to use Tomas Petricek's nice idiom for looking up Xaml controls by name with a use of the F# dynamic lookup operator (?). The telltale code for this sort of thing is as follows:

let (?) (w:Control) (s:string) : 'T = (w.FindName(s) :?> 'T)

let redButton : Button = window?redButton

let greenButton : Button = window?greenButton

let blueButton : Button = window?blueButton

let undoButton : Button = window?undoButton

let redoButton : Button = window?redoButton

let grid : Grid = window?grid

I adjusted the "Command" type to be a record - this makes the code a bit more readable.

I removed used of List/head/List.tail in favour of pattern matching, again to make the code a bit more readable.

Finally, I adjusted the code so that if you clicked "Red" when the form was already Red, no entry was pushed on the undo/redo stack, likewise for other colors..

Further below is the screen shot and code - let myself of Matt know if you think more tweaks are needed to this sample (ok, perhaps a few comments :-) ), or if you think these aren't the right tweaks to make. (Note, if using .NET 3.5, remove the references to System.Xaml).

[ Update: , if you want to remove the use of a background agent in favour of a GUI object that hold mutable state, here is the replacement code. Note this code is pretty much logically equivalent to using an agent and will be easier to debug. Methodologically it is OK and normal in F# to use mutable state to hold the state of a single-threaded GUI application ]