さて、今日はF#のシーケンスあたりでもやろうかと思ってMSDNとにらめっこしてましたが、気が変わったのでF#でWPFでMVVMパターンな凄く小さなアプリケーションを作ってみることにしました。
作るもの
仕様は、以下のような感じです。
- テキストボックスが2つあって数字を入力できる
- 入力値の検証はしない
- ボタンをおすと足し算をして結果をテキストブロックに表示する
これ以上ないくらいシンプルです。一応M V VMの役割分担は以下のようにしました
- Model
- 足し算ロジック
- ViewModel
- Viewからの入力を受けとる
- Modelに計算処理を委譲する
- コマンドでViewからのアクションを受け取る
- View
- 入力用のインターフェースとボタンを持ってる
妥協点
この試みを行う段階でいくつか妥協?した点があります。
ということで作ってみます
まず、F#のプロジェクトを作成します。名前はWpfMVVMFsにしました。
このプロジェクトに以下の参照を追加します。
- WindowsBase
- PresentationCore
- PresentationFramework
- System.Xaml
そして、プロジェクトのプロパティからWindowsアプリケーションに変更します。
続けてViewのプロジェクトを作成します。これはC#のWPFユーザコントロールライブラリを選択します。名前はWpfMVVMFs.Viewsにしました。最初デフォルトで作成されているUserControl1.xamlは削除します。
このプロジェクトにはNuGetを使ってPrismの参照を追加します。
Prismの参照を追加したら、WpfMVVMFsプロジェクトにWpfMVVMFs.Viewsプロジェクトを参照に追加します。
ここまで出来たら画面が出る所まで作ってしまいます。まず、WpfMVVMFsプロジェクトにApp.fsというファイルを作成します。ここにアプリケーションクラスを追加します。App.fsファイルはProgram.fsよりソリューションエクスプローラ上で上に持っていきます。これはF#が前に定義されてるものじゃないと使えないという制限があるからです。上への持っていきかたはApp.fsを選択した状態でAlt+↑で移動できます。
次に画面を作ります。画面だけは・・・C#のプロジェクトで普通にWPFのWindowを作成します。C#のコードには一切触らないのでよしとします。MainWindow.xamlを作成します。
そしてWpfMVVMFsプロジェクトのApp.fsを以下のようにします。
namespace WpfMVVMFs open System.Windows open WpfMVVMFs.Views // Appクラス エントリポイントみたいなもの type App() as this = inherit Application() do // Startupイベントでウィンドウを表示 this.Startup.Add <| fun e -> let w = MainWindow() w.Show()
次に、Program.fsでAppクラスを使ってWPFのアプリケーションを開始させる処理を書きます。
open System open WpfMVVMFs [<STAThread>] do let app = App() app.Run() |> ignore
次にViewModelとModelを作ります。WpfMVVMFs.ViewModelsという名前でF#のクラスライブラリのプロジェクトを作成します。デフォルトで作成されてるF#のファイルは削除したあとにPrismへの参照を追加します。NuGetが使えれば楽なのですが、悲しいことにF#のプロジェクトはサポートしてくれてないみたいなので、Viewのプロジェクトで参照している奴を拝借して追加します。PrismのDLLはソリューションフォルダのpackagesの下にあるので、それを追加しました。後は、WPF関連のDLLも参照に追加します。
- WindowsBase
- PresentationCore
- PresentationFramework
- System.Xaml
そしたらまず、Modelを作っていきます。Modelは別プロジェクトにしても良かったのですが、めんどくさいのでViewModelのプロジェクトに入れてしまいます。Calc.fsという名前でファイルを作成したら、以下のコードを書きます。足し算するだけなので簡単です。
namespace WpfMVVMFs.Models type Calc() = member this.Add(x, y) = x + y
次にViewModelクラスを作成します。これはMainWindowViewModel.fsという名前でファイルを作成して以下のように記述します。Prismのクラスをふんだんに使っています。
namespace WpfMVVMFs.ViewModels open System open Microsoft.Practices.Prism.ViewModel open Microsoft.Practices.Prism.Commands open WpfMVVMFs.Models // PrismのINotifyPropertyChangedの実装クラスを使う type MainWindowViewModel() as this = inherit NotificationObject() // モデル let model = Calc() // 計算結果 let mutable answer = 0 // 左辺値 let mutable lhs = 0 // 右辺値 let mutable rhs = 0 // 計算コマンド let calcCommand = DelegateCommand(fun() -> this.Calc()) // 計算結果のプロパティ member this.Answer with get() = answer and set(v) = answer <- v base.RaisePropertyChanged("Answer") // 左辺値のプロパティ member this.Lhs with get() = lhs and set(v) = lhs <- v base.RaisePropertyChanged("Lhs") // 右辺値のプロパティ member this.Rhs with get() = rhs and set(v) = rhs <- v base.RaisePropertyChanged("Rhs") // 計算コマンドのプロパティ member this.CalcCommand with get() = calcCommand // 計算処理 member this.Calc() = this.Answer <- model.Add(lhs, rhs)
ViewModelが出来たのでViewのプロジェクトとメインのWpfMVVMFsプロジェクトからViewModelのプロパティへの参照を追加しておきます。そして、Viewを作ります。見た目には今回は拘らないので手書きXAMLでさくっと行きました。BindingだけはVisual Studioのプロパティエディタの力をかりました。便利ですよねやっぱり。
<Window x:Class="WpfMVVMFs.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:WpfMVVMFs.ViewModels;assembly=WpfMVVMFs.ViewModels" Title="F# MVVM" Height="300" Width="300"> <Window.DataContext> <vm:MainWindowViewModel /> </Window.DataContext> <StackPanel> <TextBox Text="{Binding Path=Lhs}" /> <TextBlock Text="+" /> <TextBox Text="{Binding Path=Rhs}" /> <TextBlock Text="=" /> <TextBlock Text="{Binding Path=Answer}" /> <Button Content="計算" Command="{Binding Path=CalcCommand}" /> </StackPanel> </Window>
さて、これで完成です!実行すると、以下のようなウィンドウが表示されます。
正確にテキストボックスに整数値を入れて計算ボタンを押すと、見事計算結果が表示されました!
感想
この程度ではF#でもC#でも大差ない感じです。もっとロジックがあるとF#の強みがいきてくると思うのですが、この時点ではIDEのサポートがプアな状態でMVVMアプリを作ってるという感じであまり有難味はありません。でもまあネタとしては満足です。
過去記事
- 手軽なスクリプト言語としてのF#
- 手軽なスクリプト言語としてのF# その2
- 手軽なスクリプト言語としてのF# その3
- 手軽なスクリプト言語としてのF# その4
- 手軽なスクリプト言語としてのF# その5
- 手軽なスクリプト言語としてのF# その6
- 手軽なスクリプト言語としてのF# その7
- 手軽なスクリプト言語としてのF# その8「レコード」
- 手軽なスクリプト言語としてのF# その9「クラス」
- 手軽なスクリプト言語としてのF# その10「継承・アブストラクトクラス」
- 手軽なスクリプト言語としてのF# その11「インターフェースと演算子のオーバーロード」
- 手軽なスクリプト言語としてのF# その12「ラムダ式とイベント」
- 手軽なスクリプト言語としてのF# その13「オブジェクト初期化子みたいなの」
- 手軽なスクリプト言語としてのF# その14「合成演算子とパイプ演算子」
- 手軽なスクリプト言語としてのF# その15「WPFしてみた」
- 手軽なスクリプト言語としてのF# その16「総称型 ジェネリック」
- 手軽なスクリプト言語としてのF# その17「リスト」
- 手軽なスクリプト言語としてのF# その18「オプション型」
- 手軽なスクリプト言語としてのF# その19「参照型よりオプション型って安全?」
- 手軽なスクリプト言語としてのF# その20「パターンマッチ」
- 手軽なスクリプト言語としてのF# その21「再帰とループ」
- 手軽なスクリプト言語としてのF# その22「列挙型」
- 手軽なスクリプト言語としてのF# その23「アクティブパターン」