/TrivialBehinds

Primary LanguageC#MIT LicenseMIT

TrivialBehinds

Build Status

A Micro Dependency Injector to make Windows Forms development in F# cheap and fun. Or even in C#.

You may also want to read a related blog post on creating a Windows Forms GUI with F#.

Installation

Available on Nuget: https://www.nuget.org/packages/TrivialBehinds/

Elevator pitch

  • You want to use the Visual Studio Forms Designer because hand writing the GUI creation code is messy.

  • You don't want to couple the GUI code with Forms class because the Form class should be mostly autogenerated. Logic, event binding code etc. lives elsewhere.

  • That elsewhere can be in another project, e.g. in an F# application that obviously needs to be compiled separately.

Use case stories

  • As an aspiring F# programmer, I have avoided doing Forms stuff because it's only properly supported in C# (with Designer)

  • As a succesfull but aging C# programmer, I don't want to go down the rabbit hole of the more sophisticated, but boilerplate and abstraction ridden archituctures (Model-View-Presenter, MVVM, ...).

Both of these beautiful dreams can be realized with a tiny library, written expressly for this purpose.

Hybrid F# - C# project

  • Create solution with two projects: DesignerPart.csproj (a C# Forms application) and FsMain.fsproj (an F# console application).
  • Draw and test the UI in DesignerPart
  • Change all the controls listed on Form1.Design.cs to public

Now, TrivialBehinds steps in. In Form1.cs, after InitializeComponent call in ctor, we'll add the only bit of C# code (call to CreateComponentBehind) we need:

public Form1()
{
    InitializeComponent();
    TrivialBehinds.CreateComponentBehind(this);
}
  • it requests the instantiation of a "behind" class that corresponds to the form class "Form1". This "behind" class will hook up the events and implements all the UI logic in F#. Note that the DesignerPart doesn't need dll or project reference to the "behind" part.

The F# side

Huh, now to wash away that dirty feeling of authoring C# by switching to F# side:

  • Create "New F# Console application" (there is no Forms option)
  • Add a project reference to DesignerPart (because you will find both Form1 and Form1Ui class there)
  • Implement the "Behind" part, which has all the real logic:
type Form1Behind(ui: Form1) =
    let mutable counter = 0

    let showCount() =
        ui.label1.Text <- sprintf "Count is: %d" counter

    do
        ui.btnPlus.Click.Add <|
            fun _ ->
                counter <- counter+1
                showCount()
        ui.btnMinus.Click.Add <|
            fun _ ->
                counter <- counter-1
                showCount()
  • Modify the 'main' like so:
[<EntryPoint; STAThread>]
let main argv =
    TrivialBehinds.RegisterBehind<Form1, Form1Behind>()
    use form = new Form1()
    Application.Run(form)
    0

That's it! Set the F# side as startup project and press f5 to debug to your heart's content.

If that extra terminal window on launch bothers you, change outputtype form Exe to WinExe in FsMain.fsproj:

<OutputType>WinExe</OutputType>

Extra notes

  • Windows Forms apps have fuzzy font rendering on Windows 10, unless you do a trick and compile with .NET Framework 4.7. Don't even think about publishing a Windows Forms app without this.