Windows Phone 7とコマンドバインディング

WPF/SilverlightはデータバインディングによりViewとViewModel、ViewModelとModel、互いの疎結合を保ったままでデータの同期を可能にしている。

データバインディングそのものは古のWindowsFormsから実装されており知っているのだが、WPF/SilverlightはMVVMを実装するためにもう一つの重要な機構を持っている。

コマンドバインディング

コマンドとは検索や更新、登録、削除などモデル-エンティティのCRUDビジネスロジックの呼出しを抽象化したものだが、WPF/Silverlightのコマンドバインディングはコマンドを抽象化したICommandインタフェースの実装クラスのインスタンスXAMLからバインド、実行することができる。


    

このXAMLの語彙により、コードビハインドも含めて一切のコードを書かず、Buttonコントロールが押下された際にFooCommandを起動することができる。


本日記では以前から何度も書いてきたが、私はイベントドリブンでアプリケーションを書くのが嫌いだ。見通しが悪くなるのは勿論だが、それ以上に設計上の機能とデータの祖結合を阻害するからだ。WPF/Silverlightのデータバインディングとコマンドバインディングはイベント中心のプログラミングを改善し、コンポーネントの結合を緩くしてテスタビリティを向上する。

さて、この素晴らしいコマンドバインディングをWindowsPhone7でも使いたい。WindpwsPhone7はSliverlightベースのランタイムであり、Mangoではそのバージョンは3から4に上がる。従って上記コマンドバインディングが可能なはずである。

だが、WPFSilverlightではかなり実装に差がありSilverlight 3では使えず、4では標準でコマンドのバインドが可能なコントロールが限定されており、使用できるのは

    • Button
    • MenuItem
    • HyperLink

これだけとなっている。

ということでまずはSliverlight 4となったWindowsPhone7プラットホーム上でSilverlight4と同等のコマンドバインディングが可能かどうか、検証してみることにする。


DataBindApp1によるコマンドバインディングの検証

アプリケーション"DataBindApp1"はWizardにより大部分のコードを生成したものだ。画面はPage1.xamlから起動し、ボタンが押下されたらPage2.xamlに遷移する極めて簡単なものだ。

ただし、ボタンを押下された場合のページのナビゲーションをコマンドとして扱い、ボタンからコマンドバインディングでナビゲーションを実施するコマンドを実行するためにICommandインタフェースを実装するNavigateCommandクラスを使用する。従って、コードビハインド/イベントハンドラの記述は一切無い。

また、コマンドを実行するクラスではXAML画面のナビゲーションを実現するために、ページ(PhoneApplicationPage)への参照が必要なため、ICommand#ExecuteのパラメタへPhoneApplicationPageクラスのインスタンスをバインドするのもポイントだ。(通常はこのような作りにはしないだろう。今回は実験のためにあえてそうしている)

    • Page1.xaml (邪魔なので関係の無いXMLネームスペースは除去)

    
        
    

        
        
            
            
        

                
            
            
        

                
            
  • NavigateCommand.cs
namespace DataBindApp1.Command
{
    public class NavigateCommand : ICommand
    {
        public bool CanExecute(object parameter)
        {
            return true;
        }
        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            PhoneApplicationPage page = parameter as PhoneApplicationPage;
            page.NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
        }
    }
}

実際にコントロールがバインドされたコマンドを実行するため、ICommandインタフェースの実装が必須となる。また、しつこいようだがここにもビューのイベントハンドラやそのデリゲートを捕捉するようなコードは一切無い。


さて、コードのポイントはいくつかあるが、まずはXAMLから。


         x:Key=cmd/>
    

XAMLのスタティックリソース要素によりXAML内でNavigateCommandクラスのインスタンスを生成しており、他のFrameworkElementからx:Key属性で指定した"cmd"という名前で参照することができる。

    

ここがコマンドバインディングの記述であり、Commandプロパティの拡張構文により先ほど解説したスタティックリソースをここで参照している。更にCommandParameterプロパティをBinding拡張構文により記述しており、Binding#ElementNameプロパティによってこのXAMLで記述されているページのインスタンスそのものを参照している。

ここで、ページのインスタンスとして指定している名前"PhonePage1"だが、どこで指定しているのだろう。

答えはルート要素であるPhoneApplicationPageの属性"x:Name"である。

PhonePage1">


これらの記述により、ボタンをコマンドのトリガとして使ったコマンドバインディングは実現可能なことが解った。
XAMLは強力でありこれだけの事をコードを一行も書くこと無く済ませてしまう。

今後、WindowsPhone7(Silverlight4)で出来ないケースも含めて、どのようにMVVMを実現していくかを考察してみようと思う。※


※先達の方々が既にいろいろと試しておりほぼ答えは出ているのだが、それを追って理解しないと意味が無い。