Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I enable a WinForms or WPF project in F#?

I have the very latest version of Visual Studio 2017 installed. I selected F# language support and F# desktop support. After restarting and going to File -> New Project I was expecting to see an option to start a new WPF or WinForms project for F# but I don't have any such options. Only console, library, ASP.NET core, and the tutorial.

How can I enable or find the new project templates for F# desktop applications?

like image 206
Matthew MacFarland Avatar asked May 16 '18 14:05

Matthew MacFarland


People also ask

Can you mix WPF and Windows Forms?

Yes you can, both Windows Forms within a WPF application, and WPF controls within Windows Forms.


4 Answers

You can create both WPF and WinForms applications in F#. There is no template for it, but it is certainly possible and many people do this. To create a simple WinForms application, you can do the following:

  • Create a new console application. Go to project properties and change "Output type" in the "Application" page from "Console application" to "Windows Application"

  • Right click on "References" in the project and add reference to "System.Windows.Forms" and "System.Drawing" (in the "Framework" tab).

  • Replace the code in Program.fs with the following:

    open System.Windows.Forms
    
    let f = new Form()
    f.Controls.Add(new Label(Text="Hello world!"))
    Application.Run(f)
    

For anything bigger, you will probably want to use a nice F# GUI library such as the FsXaml project mentioned by Isaac or Gjallarhorn, but the basic first steps will be the same - create a console project, make it a windows project and then add relevant references to GUI frameworks.

like image 154
Tomas Petricek Avatar answered Oct 05 '22 07:10

Tomas Petricek


As far as I know, there are no "out of the box" templates for WPF or WinForms on F# but (at least in VS2015) there were a set of community templates for WPF apps. To be honest, you don't need a template really, especially if you use the FSXaml project which makes it pretty easy to do it by hand (https://github.com/fsprojects/FsXaml).

like image 20
Isaac Abraham Avatar answered Oct 05 '22 07:10

Isaac Abraham


This isn't an easy answer. There is no built-in project template for those projects because neither the Windows Forms nor the WPF designer understand F#. We just got the ASP.NET Core templates for F# so we shouldn't complain... much.

On the other hand, a Winforms or WPF application is just C# code. Anything you do in the designer ends up as code in the form file. A Winforms or WPF application are "just" Console applications that set up the proper environment and create the forms and controls.

This is actually easier using XAML and WP, especially if you use MVVM or a similar architecture. That's because you can develop the XAML markup separately from the F# code and bind your data to XAML. You can even create a WPF Class library project, create Windows, pages and controls there without any code and then use them in your F# project.

There are a few libraries that make this easier. One option is Elmish.WPF. You can create the XAML pages and windows in a separate project and then create models etc in F#, bind them together and start the main form. If you check the repo's example most of the code defines records and commands as F# functions, eg :

type ClockMsg =
    | Tick of DateTime

type ClockModel =
    { Time: DateTime }

type Msg =
    | ClockMsg of ClockMsg
    | Increment
    | Decrement
    | SetStepSize of int

type Model = 
    { Count: int
      StepSize: int
      Clock: ClockModel }

and

let init() = { Count = 0; StepSize = 1; Clock = { Time = DateTime.Now }}

let clockUpdate (msg:ClockMsg) (model:ClockModel) =
    match msg with
    | Tick t -> { model with Time = t }

let update (msg:Msg) (model:Model) =
    match msg with
    | Increment -> { model with Count = model.Count + model.StepSize }
    | Decrement -> { model with Count = model.Count - model.StepSize }
    | SetStepSize n -> { model with StepSize = n }
    | ClockMsg m -> { model with Clock = clockUpdate m model.Clock }

Binding them together is relatively simple :

let view _ _ = 
    let clockViewBinding : ViewBindings<ClockModel,ClockMsg> =
        [ "Time" |> Binding.oneWay (fun m -> m.Time) ]

    [ "Increment" |> Binding.cmd (fun m -> Increment)
      "Decrement" |> Binding.cmdIf (fun m -> Decrement) (fun m -> m.StepSize = 1)
      "Count" |> Binding.oneWay (fun m -> m.Count)
      "StepSize" |> Binding.twoWay (fun m -> (double m.StepSize)) (fun v m -> v |> int |> SetStepSize)
      "Clock" |> Binding.vm (fun m -> m.Clock) clockViewBinding ClockMsg ]

And running the entire application

[<EntryPoint;STAThread>]
 let main argv = 
     Program.mkSimple init update view
     |> Program.withSubscription subscribe
     |> Program.runWindow (Elmish.CounterViews.MainWindow())

The code above comes from the Counter sample. The XAML views are defined in the CounterViews project. You can even delete the .cs files and the project will compile just fine.

like image 24
Panagiotis Kanavos Avatar answered Oct 05 '22 06:10

Panagiotis Kanavos


And coming "soon" to an IDE near you:

“You can expect two releases from us in 2019, .NET Core 3 and .NET Framework 4.8.”

https://blogs.msdn.microsoft.com/dotnet/2018/05/07/net-core-3-and-support-for-windows-desktop-applications/

like image 24
Scott Hutchinson Avatar answered Oct 05 '22 06:10

Scott Hutchinson