概要

MVVM Light Toolkitを使ってViewModelとModelをF#で記述した足し算を行うだけのシンプルなアプリケーションの実装例です。サンプルアプリケーションは以下のような形のものになります。

以下に、サンプルに含まれるプロジェクトの概要を示します。

ポイント

F#でのプロパティやコマンドの記述を簡略化するために、MVVM Light Toolkitで提供されているViewModelBaseを継承して、以下のようなヘルパーメソッドを追加したFsViewModelBaseクラスを定義しています。

 

C#
namespace Okazuki.Fsharp.Mvvm 
{ 
    using System; 
    using GalaSoft.MvvmLight; 
    using GalaSoft.MvvmLight.Command; 
 
    /// <summary> 
    /// F#でのViewModelの定義の簡略化のためのメソッドを追加したViewModelの基本クラス。 
    /// </summary> 
    public class FsViewModelBase : ViewModelBase 
    { 
        /// <summary> 
        /// プロパティの値の設定と変更通知を行います。 
        /// </summary> 
        /// <typeparam name="T">プロパティの型</typeparam> 
        /// <param name="field">フィールド</param> 
        /// <param name="value">プロパティにセットする値</param> 
        /// <param name="propertyName">プロパティ名</param> 
        protected void SetAndNotify<T>(ref T field, T valuestring propertyName) 
        { 
            if (Equals(field, value)) 
            { 
                return; 
            } 
 
            field = value; 
            base.RaisePropertyChanged(propertyName); 
        } 
 
        /// <summary> 
        /// RelayCommandを作成、または取得します。 
        /// </summary> 
        /// <param name="field">フィールド</param> 
        /// <param name="execute">コマンドの実行メソッド</param> 
        /// <param name="canExecute">コマンドの実行可否判定メソッド</param> 
        /// <returns>作成または取得されたコマンド</returns> 
        protected RelayCommand GetOrCreateCommand( 
            ref RelayCommand field, 
            Action execute, 
            Func<bool> canExecute) 
        { 
            if (field != null) 
            { 
                return field; 
            } 
 
            return field = new RelayCommand(execute, canExecute); 
        } 
 
        /// <summary> 
        /// RelayCommandを作成、または取得します。 
        /// </summary> 
        /// <param name="field">フィールド</param> 
        /// <param name="execute">コマンドの実行メソッド</param> 
        /// <returns>作成または取得されたコマンド</returns> 
        protected RelayCommand GetOrCreateCommand( 
            ref RelayCommand field, 
            Action execute) 
        { 
            return GetOrCreateCommand(ref field, execute, () => true); 
        } 
 
        /// <summary> 
        /// RelayCommandを作成、または取得します。 
        /// </summary> 
        /// <typeparam name="T">コマンドの引数の型</typeparam> 
        /// <param name="field">フィールド</param> 
        /// <param name="execute">コマンドの実行メソッド</param> 
        /// <param name="canExecute">コマンドの実行可否判定メソッド</param> 
        /// <returns>作成または取得されたコマンド</returns> 
        protected RelayCommand<T> GetOrCreateCommand<T>( 
            ref RelayCommand<T> field, 
            Action<T> execute, 
            Predicate<T> canExecute) 
        { 
            if (field != null) 
            { 
                return field; 
            } 
 
            return field = new RelayCommand<T>(execute, canExecute); 
        } 
 
        /// <summary> 
        /// RelayCommandを作成、または取得します。 
        /// </summary> 
        /// <typeparam name="T">コマンドの引数の型</typeparam> 
        /// <param name="field">フィールド</param> 
        /// <param name="execute">コマンドの実行メソッド</param> 
        /// <returns>作成または取得されたコマンド</returns> 
        protected RelayCommand<T> GetOrCreateCommand<T>( 
            ref RelayCommand<T> field, 
            Action<T> execute) 
        { 
            return GetOrCreateCommand<T>(ref field, execute, v => true); 
        } 
    } 
} 
 
 
 

 

いくつか、メソッドが定義されていますが、ポイントとなるメソッドは2つになります。1つはプロパティのsetterで使用するメソッド。もう1つはRelayCommand型のプロパティで使用するメソッドです。setterで使用するメソッドはSetAndNotifyメソッドで、プロパティのsetterに書くプロパティの変更通知などの冗長な処理を肩代わりしてくれます。RelayCommand型のプロパティで使用するメソッドは、初回のみコマンドの生成を行い、二回目以降の呼び出しでは生成したコマンドを返すメソッドです。

上記のクラスを使用することでF#で記述するViewModelのプロパティの定義のコードは以下のようになります。

 

C#
let mutable lhs = 0 
 
/// 左辺値を取得または設定します 
member x.Lhs  
    with get() = lhs 
    and set v = x.SetAndNotify(&lhs, v, "Lhs") 
 
 
SetAndNotifyの記述だけでいい点とF#の文法の簡潔さによって非常にすっきりした定義になります。コマンドの定義も以下のようになります。
C#
let mutable addCommand = null 
 
/// 足し算コマンドを取得します 
member x.AddCommand 
    with get() = x.GetOrCreateCommand( 
                    &addCommand,  
                    x.AddExecute) 
 
/// 足し算ロジックを呼び出します。 
member private x.AddExecute() = 
    // Modelのメソッドへ委譲 
    x.Answer <- Calc.add lhs rhs 
 
 
実際には、フィールドの定義とメソッドやプロパティの定義の並びの関係で上記のようにプロパティ関連の定義やコマンドの定義をまとまった位置に書くことは出来ませんが実際のコードイメージは上記のようになります。

MVVM Light Toolkitらしく仕上げる

最後に、ViewModelLocatorを定義してF#で作成したViewModelを返すプロパティを定義します。そしてWindowを以下のように左辺値と右辺値の入力TextBoxと計算を行うボタンと答えを表示するTextBlockを置いて完成です。

 

XAML
<Window x:Class="Okazuki.Fsharp.Mvvm.Sample.MainWindow" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="MainWindow" Height="350" Width="525"  
        DataContext="{Binding Source={StaticResource Locator}, Path=Main}" SizeToContent="WidthAndHeight"    <Grid        <Grid.ColumnDefinitions            <ColumnDefinition Width="Auto" /> 
            <ColumnDefinition Width="Auto" /> 
            <ColumnDefinition Width="Auto" /> 
            <ColumnDefinition Width="Auto" /> 
            <ColumnDefinition Width="Auto" /> 
        </Grid.ColumnDefinitions> 
        <Button Content="=" Name="button1" Command="{Binding Path=AddCommand}" Grid.Column="3" Margin="5" /> 
        <TextBox Name="textBox1" Text="{Binding Path=Lhs}" MinWidth="75" Margin="5" /> 
        <TextBlock Name="textBlock1" Text="{Binding Path=Answer}" Grid.Column="4" Margin="5" VerticalAlignment="Center" /> 
        <TextBox Name="textBox2" Text="{Binding Path=Rhs}" Grid.Column="2" MinWidth="75" Margin="5" /> 
        <TextBlock Grid.Column="1" Name="textBlock2" Text="+" Margin="5" VerticalAlignment="Center" /> 
    </Grid> 
</Window> 
 
 

 

まとめ

F#は、C#やVBに比べて非常に簡潔な記述で多くの事を表現することが出来る言語です。この言語は画面の定義などにも使おうと思えば使えますが、現状ツールのサポートが追い付いていないためMVVMパターンのViewはC#などの既存の言語をベースにしたプロジェクトで作成し、残りのViewModelやModelをF#で記述するという選択肢も作成するアプリケーションの形態によってはありだと思います。

その際に、ちょっとした工夫を行うことで、この記事で示したように簡潔なコードでViewModelを定義することが出来るようになります。