dotnet 透過 Elmish.WPF 使用 F# 編寫 WPF 應用

uu365發表於2021-05-23

  本文來安利大家一個有趣而且強大的庫,透過F#和C#混合程式設計編寫WPF應用,可以在WPF中使用到F#強大的資料處理能力

  在閱讀本文之前,我期望大家已瞭解部分F#的知識。在學會C#基礎知識之下,瞭解F#基礎知識是很快的,而本文也僅僅只是用到很少的知識。大家都知道,使用F#能帶來很好的資料處理能力,從F#語法層面帶來的編寫效率提升和編寫邏輯的最佳化,而F#本身是沒有帶GUI視覺化應用程式設計的。但是F#是在dotnet體系下的,天然就可以用上dotnet系的技術,當然就包括WPF了。因此上一句話說F#本身是沒有帶GUI視覺化應用程式設計是完全錯誤的,因為F#可以非常方便調起WPF和WinForms等成熟的UI框架作為自身的視覺化應用框架

  透過Elmish.WPF庫,將可以讓開發更順,以下是一個簡單的例子。這個例子的程式碼完全放在Elmish.WPF庫裡。

  我們依然透過VS建立一個空白的C#空白WPF程式,在此例子裡面,幾乎沒有C#多少的戲份,只是為了使用C#更好的驅動WPF程式而已,因為部分初始化方法和型別等在F#寫起來的程式碼量可不少。本文的主角將交給XAML和F#這兩個語言

  開啟App.xaml.cs檔案,我們將修改本文僅有的一點C#程式碼

  public partial class App:Application

  {

  public App()

  {

  this.Activated+=StartElmish;

  }

  private void StartElmish(object sender,EventArgs e)

  {

  this.Activated-=StartElmish;

  Program.main(MainWindow);

  }

  }

  以上程式碼的Program類是在F#專案裡面編寫的,也就是說C#語言只是膠水,用來做初始化的連線

  接下來我們來建立一個簡單的介面,這個介面如下

  基本功能是點選加號和減號分別實現當前數值的加減

  大概的XAML程式碼如下

  <StackPanel Orientation="Horizontal"HorizontalAlignment="Center"VerticalAlignment="Top"Margin="0,25,0,0">

  <TextBlock Text="{Binding CounterValue,StringFormat='Counter value:{0}'}"Width="110"Margin="0,5,10,5"/>

  <Button Command="{Binding Decrement}"Content="-"Margin="0,5,10,5"Width="30"/>

  <Button Command="{Binding Increment}"Content="+"Margin="0,5,10,5"Width="30"/>

  <TextBlock Text="{Binding StepSize,StringFormat='Step size:{0}'}"Width="70"Margin="0,5,10,5"/>

  <Slider Value="{Binding StepSize}"TickFrequency="1"Maximum="10"Minimum="1"IsSnapToTickEnabled="True"Width="100"Margin="0,5,10,5"/>

  <Button Command="{Binding Reset}"Content="Reset"Margin="0,5,10,5"Width="50"/>

  </StackPanel>

  在XAML中定義了命令和值的繫結,沒有做實際的實現

  接下來建立一個F#專案,將這個專案被剛才建立的WPF專案所引用,將WPF專案作為啟動專案。執行的順序是先啟動WPF專案,進入App類裡面,在App的Activated事件,呼叫F#專案的邏輯,然後接下來就交給F#來進行資料繫結,在F#專案中,將給MainWindow附加自身作為DataContext資料用於在XAML繫結

  在F#的Program.fs檔案裡面新增如下程式碼

  module Elmish.WPF.Samples.SingleCounter.Program

  open Serilog

  open Serilog.Extensions.Logging

  open Elmish.WPF

  type Model=

  {Count:int

  StepSize:int}

  type Msg=

  |Increment

  |Decrement

  |SetStepSize of int

  |Reset

  let init=

  {Count=0

  StepSize=1}

  let canReset=(<>)init

  let update msg m=

  match msg with

  |Increment->{m with Count=m.Count+m.StepSize}

  |Decrement->{m with Count=m.Count-m.StepSize}

  |SetStepSize x->{m with StepSize=x}

  |Reset->init

  let bindings():Binding<Model,Msg>list=[

  "CounterValue"|>Binding.oneWay(fun m->m.Count)

  "Increment"|>Binding.cmd Increment

  "Decrement"|>Binding.cmd Decrement

  "StepSize"|>Binding.twoWay(

  (fun m->float m.StepSize),

  int>>SetStepSize)

  "Reset"|>Binding.cmdIf(Reset,canReset)

  ]

  let designVm=ViewModel.designInstance init(bindings())

  let main window=

  let logger=

  LoggerConfiguration()

  .MinimumLevel.Override("Elmish.WPF.Update",Events.LogEventLevel.Verbose)

  .MinimumLevel.Override("Elmish.WPF.Bindings",Events.LogEventLevel.Verbose)

  .MinimumLevel.Override("Elmish.WPF.Performance",Events.LogEventLevel.Verbose)

  .WriteTo.Console()

  .CreateLogger()

  WpfProgram.mkSimple(fun()->init)update bindings

  |>WpfProgram.withLogger(new SerilogLoggerFactory(logger))

  |>WpfProgram.startElmishLoop window

  十分簡單的邏輯,在F#定義的命令等將可以透過Elmish.WPF庫和WPF的XAML繫結,相當於XAML提供介面邏輯,而F#提供資料驅動的處理邏輯。各個語言的職責是XAML負責介面,而F#負責後臺邏輯。將XAML和F#連線起來以及應用程式的啟動是C#語言。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69997029/viewspace-2773551/,如需轉載,請註明出處,否則將追究法律責任。

相關文章