FSharp.ViewModule
我們的演示應用程式包含一個記分板。分數模型是不可變的記錄。記分板事件包含在聯合型別中。
namespace Score.Model
type Score = { ScoreA: int ; ScoreB: int }    
type ScoringEvent = IncA | DecA | IncB | DecB | NewGame
通過偵聽事件並相應地更新檢視模型來傳播更改。我們宣告一個單獨的模組來託管允許的操作,而不是像在 OOP 中那樣向模型型別新增成員。
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Score =
    let zero = {ScoreA = 0; ScoreB = 0}
    let update score event =
        match event with
        | IncA -> {score with ScoreA = score.ScoreA + 1}
        | DecA -> {score with ScoreA = max (score.ScoreA - 1) 0}
        | IncB -> {score with ScoreB = score.ScoreB + 1}
        | DecB -> {score with ScoreB = max (score.ScoreB - 1) 0}
        | NewGame -> zero 
我們的檢視模型來自 EventViewModelBase<'a>,它具有 IObservable<'a> 型別的屬性 EventStream。在這種情況下,我們要訂閱的事件是 ScoringEvent 型別。
控制器以功能方式處理事件。它的標誌性 Score -> ScoringEvent -> Score 告訴我們,每當事件發生時,模型的當前值將轉換為新值。這允許我們的模型保持純淨,儘管我們的檢視模型不是。
一個 eventHandler 負責改變檢視的狀態。繼承 EventViewModelBase<'a>,我們可以使用 EventValueCommand 和 EventValueCommandChecked 將事件連線到命令。
namespace Score.ViewModel
open Score.Model
open FSharp.ViewModule
type MainViewModel(controller : Score -> ScoringEvent -> Score) as self = 
    inherit EventViewModelBase<ScoringEvent>()
    let score = self.Factory.Backing(<@ self.Score @>, Score.zero)
    let eventHandler ev = score.Value <- controller score.Value ev
    do
        self.EventStream
        |> Observable.add eventHandler
    member this.IncA = this.Factory.EventValueCommand(IncA)
    member this.DecA = this.Factory.EventValueCommandChecked(DecA, (fun _ -> this.Score.ScoreA > 0), [ <@@ this.Score @@> ])
    member this.IncB = this.Factory.EventValueCommand(IncB)
    member this.DecB = this.Factory.EventValueCommandChecked(DecB, (fun _ -> this.Score.ScoreB > 0), [ <@@ this.Score @@> ])
    member this.NewGame = this.Factory.EventValueCommand(NewGame)
    member __.Score = score.Value
檔案後面的程式碼(* .xaml.fs)是將所有內容放在一起的地方,即在 MainViewModel 中注入更新函式(controller)。
namespace Score.Views
open FsXaml
type MainView = XAML<"MainWindow.xaml">
type CompositionRoot() =
    member __.ViewModel = Score.ViewModel.MainViewModel(Score.Model.Score.update)
CompositionRoot 型別用作 XAML 檔案中引用的包裝器。
<Window.Resources>
    <ResourceDictionary>
        <local:CompositionRoot x:Key="CompositionRoot"/>
    </ResourceDictionary>
</Window.Resources>
<Window.DataContext>
    <Binding Source="{StaticResource CompositionRoot}" Path="ViewModel" />
</Window.DataContext>