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>