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#.
Available on Nuget: https://www.nuget.org/packages/TrivialBehinds/
-
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.
-
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.
- 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.
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>
- 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.