WindowsストアアプリでMVVMを扱う。 MVVM Light Toolkit Messenger編
昨年の年末、コミックマーケットC85で出した
Far East Developer Review - DevLOVE Pub the booth
に寄稿した「MVVMで始めるWindowsストアアプリ」の続きになります。
(「MVVMで始めるWindowsストアアプリ」の記事が読めるFar East Developer Review - DevLOVE Pub the boothは DevLOVE Pub the boothにて頒布しています。)
Messengerパターンを扱う
Messengerパターンは、ViewModelからViewに要求を送る場合にとるパターン(のよう)です。
Messageを詰めるオブジェクト(Messageオブジェクト)を用意し、ViewModelにてMessageオブジェクトを送ると、Messageオブジェクトを受け取るように定義したViewにて、Messageオブジェクトを受け取れる仕組みです。
(詳しくはこのあたりとか読んでみるといいかも。 いまさら聞けない「MVVM + Messenger パターン」超入門 - present)
今回はViewModelから別のViewModelにMessageを飛ばし、受け取ったViewModelでMessageオブジェクトの中身を表示するやり方を書いてみます。
まずは、ViewModelLocator.csにViewModelの定義を追加します。
/// <summary> /// This class contains static references to all the view models in the /// application and provides an entry point for the bindings. /// <para> /// See http://www.galasoft.ch/mvvm /// </para> /// </summary> public class ViewModelLocator { static ViewModelLocator () { ServiceLocator . SetLocatorProvider (() => SimpleIoc. Default ); if (ViewModelBase . IsInDesignModeStatic ) { SimpleIoc .Default . Register< IDataService , Design . DesignDataService >(); } else { SimpleIoc .Default . Register< IDataService , DataService > (); } SimpleIoc .Default . Register< MainViewModel >(); SimpleIoc .Default . Register< UserControlViewModel > (); } /// <summary> /// Gets the Main property. /// </summary> [ System .Diagnostics . CodeAnalysis. SuppressMessage ( "Microsoft.Performance" , "CA1822:MarkMembersAsStatic" , Justification = "This non-static member is needed for data binding purposes." )] public MainViewModel Main { get { return ServiceLocator . Current. GetInstance <MainViewModel > (); } } /// <summary> /// Gets the Main property. /// </summary> [ System .Diagnostics . CodeAnalysis. SuppressMessage ( "Microsoft.Performance" , "CA1822:MarkMembersAsStatic" , Justification = "This non-static member is needed for data binding purposes." )] public UserControlViewModel UserControl { get { return ServiceLocator . Current. GetInstance <UserControlViewModel > (); } } /// <summary> /// Cleans up all the resources. /// </summary> public static void Cleanup () { } }
Message送信元のViewにあるGridViewで選択されたItemをMessage受信先のUserControlで受け取り表示させます。 今回は手間を省きたかったので、受信先のUserControlは送信元のViewで表示するようにしています。
< Grid Grid.Row= "1" > < Grid.RowDefinitions > < RowDefinition Height = "Auto" /> </ Grid.RowDefinitions > < Grid.ColumnDefinitions > < ColumnDefinition Width= "500" /> < ColumnDefinition Width= "*" /> </ Grid.ColumnDefinitions > < GridView x : Name= "ItemsGridView" Margin ="120, 0, 0, 0"Grid.Row ="0" Grid.Column ="0" ItemsSource="{ Binding Items} " SelectedItem="{ Binding SelectedGridItem , Mode = TwoWay} " > < GridView.ItemTemplate > < DataTemplate> < TextBlock Text ="{ Binding Name } " Width = "250"Height= "60" FontSize ="40" /> </ DataTemplate> </ GridView.ItemTemplate > </ GridView> < ml: UserControlView Grid.Row ="0" Grid.Column ="1" /> </ Grid >
< UserControl x :Class = "MvvmLight4.UserControlView" xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns :x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns :local = "using:MvvmLight4" xmlns :d = "http://schemas.microsoft.com/expression/blend/2008" xmlns :mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns :common = "using:MvvmLight4.Common" xmlns :ignore = "http://www.ignore.com" mc :Ignorable = "d ignore" d :DesignHeight = "300" d :DesignWidth = "400" DataContext ="{Binding UserControl , Source ={ StaticResourceLocator}} " > < Grid> < Grid.RowDefinitions > < RowDefinition Height = "50" /> </ Grid.RowDefinitions > < Grid.ColumnDefinitions > < ColumnDefinition Width= "200" /> </ Grid.ColumnDefinitions > < TextBlock Grid.Row = "0" Grid.Column = "0" Text ="{ Binding ReceiveItem . Name} " /> </ Grid> </ UserControl >
さて、ここからが本番。 MainPage.xamlで定義しているGridViewで選択されたItemをMessageとして送信していきます。
まずは、Messageオブジェクトの定義から。
public class ItemChangeMessage { public ItemChangeMessage ( Item item ) { Item = item; } public Item Item{ get ; private set; } }
このItemChangeMessageを送信するMainViewModel.csはこんな感じ。
public class MainViewModel : ViewModelBase { private readonly IDataService _dataService ; private List < Item> _items; private Item _selectedGridItem ; /// <summary> /// Initializes a new instance of the MainViewModel class. /// </summary> public MainViewModel ( IDataService dataService ) { _dataService = dataService; var items = new List < Item> (); items .Add ( new Item ("hogehoge" )); items .Add ( new Item ("hugahuga" )); Items = items; this .PropertyChanged += MainViewModel_PropertyChanged ; } void MainViewModel_PropertyChanged ( object sender , System . ComponentModel .PropertyChangedEventArgs e) { MessengerInstance . Send( new ItemChangeMessage ( _selectedGridItem )); } public List < Item> Items { get { return _items ; } set { if (_items != value ) { _items = value; RaisePropertyChanged ( "Items" ); } } } public Item SelectedGridItem { get { return _selectedGridItem ; } set { if (_selectedGridItem != value ) { _selectedGridItem = value ; RaisePropertyChanged ( "SelectedGridItem" ); } } } }
GridViewで選択しているItemが変わる→SelectedGridItemが変わる→MainViewModelのプロパティが変わり、MainViewModel_PropertyChangedイベントが走る。→イベント内でMessageを送る。
という流れですね。
MainViewModelで送信したMessageを、UserControlViewModelで受けます。
public class UserControlViewModel : ViewModelBase { private readonly IDataService _dataService ; private Item _item; public UserControlViewModel ( IDataService dataService ) { _dataService = dataService; MessengerInstance . Register< ItemChangeMessage > (this ,this. GetItemChangeMessage ); } public void Initialize() { } public void GetItemChangeMessage (ItemChangeMessage message) { ReceiveItem = message. Item ; } public Item ReceiveItem { get { return _item; } set { if (_item != value ) { _item = value; RaisePropertyChanged ( "ReceiveItem" ); } } } }
UserControlViewModel.csのコンストラクタで、ItemChangeMessageを受信したときに呼び出すメソッドを登録しています。 これで、MainViewModel.csからItemChangeMessageを送信したら、UserControlViewModelで受け取れるようになります。 受け取ったItemChangeMessageは、GetItemChangeMessageメソッドでMessageの中身を取り出し、プロパティに入れてViewに通知をしています。
これで、MVVM + Messengerパターンを動かすことができるようになりました。
もともと、「MVVMで作るWindowsストアアプリ」を執筆していたときは「Caliburn.micro使えないじゃんうわーん」だったのですが、どうもNuGet経由がダメだということかもしれないです。
なので、今度はCaliburn.microを使ってWindowsストアアプリを試してみようかなと思ってます。
その前に、Windowsストアアプリのちゃんとした作り方を勉強したいなぁ。