Auch mit Visual Studio 2022 ist F# noch immer ein Stiefkind was WinForms Entwicklung angeht, trotzdem sind Dinge wie gehabt wesentlich knapper, präziser und je nach Geschmack schöner mit F# abzubilden – im speziellen eine sehr einfache Kiosk App die nichts anderes als eine Seite Anzeigen soll von der man am besten nicht “entkommt”:

  • Kein Kontextmenü
  • Keine Downloads bzw. nur für Autostart (bestimmte Dateiextension)
  • Keine neuen Fenster
  • Nur bestimmte URLs (Regex) erlaubt

Ergebnis:

open System
open System.Windows.Forms
open System.IO
open System.Text.RegularExpressions
open System.Diagnostics
open Microsoft.Web.WebView2.WinForms
open Microsoft.Web.WebView2.Core

let InitForm (env:CoreWebView2Environment) =
   let webViewForm = new Form(Text = "Kiosk Titel", Width=int (float Screen.PrimaryScreen.Bounds.Width * 0.8), Height=int (float Screen.PrimaryScreen.Bounds.Height * 0.6), StartPosition=FormStartPosition.CenterScreen, ShowIcon=false)

   let webView = new WebView2()
   webView.Dock <- System.Windows.Forms.DockStyle.Fill
   webView.Location <- new System.Drawing.Point(0, 0)
   webViewForm.Controls.Add webView
   webView.EnsureCoreWebView2Async(env) |> ignore
   webView.CoreWebView2InitializationCompleted.AddHandler(fun sender args ->
      webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled <- false
      webView.CoreWebView2.Settings.AreDevToolsEnabled <- false
      webView.CoreWebView2.Settings.AreBrowserAcceleratorKeysEnabled <- false
      webView.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled <- false
      webView.CoreWebView2.Settings.AreHostObjectsAllowed <- false
      webView.CoreWebView2.Settings.IsPasswordAutosaveEnabled <- false
      webView.CoreWebView2.Settings.IsGeneralAutofillEnabled <- false
      webView.CoreWebView2.Settings.IsWebMessageEnabled <- false
     
      webView.CoreWebView2.DownloadStarting.AddHandler(fun downloadsender downloadargs ->
         let deferral = downloadargs.GetDeferral()
         System.Threading.SynchronizationContext.Current.Post(fun _ ->
            downloadargs.Handled <- true     // no download dialog
            downloadargs.DownloadOperation.StateChanged.AddHandler(fun statesender stateargs -> 
               if (downloadargs.DownloadOperation.State = CoreWebView2DownloadState.Completed) then
                  if (System.IO.Path.GetExtension(downloadargs.DownloadOperation.ResultFilePath).ToUpper() = ".XXX") then // valid extensions to start
                     let proc=new ProcessStartInfo(downloadargs.DownloadOperation.ResultFilePath)
                     proc.UseShellExecute <- true
                     Process.Start(proc) |> ignore
            )
            deferral.Dispose()
         , null)
      )

      webView.CoreWebView2.NavigationStarting.AddHandler(fun navigatesender navigateargs ->
         if (Regex.Match(navigateargs.Uri,"^https://(mysite|myothersite)\.local/.*", RegexOptions.IgnoreCase).Success = false) then navigateargs.Cancel <- true
      )

      webView.CoreWebView2.NewWindowRequested.AddHandler(fun newwindowsender newwindowargs ->
         newwindowargs.Handled <- true      // do not open window
      )
      webView.CoreWebView2.Navigate "https://mysite.local" |> ignore
   )
   webViewForm


[<EntryPoint; STAThread>]
let main argv = 
   let userDataFolder = $"{Path.GetTempPath()}\KioskWebUserData"
   let options = new CoreWebView2EnvironmentOptions("--kiosk")
   let env=CoreWebView2Environment.CreateAsync("",userDataFolder,options).GetAwaiter().GetResult()

   Application.EnableVisualStyles()
   Application.SetCompatibleTextRenderingDefault(false)
   Application.Run(InitForm env)
   0

Projektdatei:

  
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1020.30" />
  </ItemGroup>

</Project>