step2:ViewとModelの仲介役
アプリの外観MainPage.xaml(View)を基盤となるデータ:Counter(Model)から切り離し、その両者を結び付ける仲介役となるViewModelを作ります。ひな型がこちら、ファイル名/名前空間/クラス名は適宜書き換えて使ってください(Visual Studio 2012ではCommonフォルダに生成されたCommon::BindableBaseを導出すればよかったのですが、2013ではCommon::BindableBaseが生成されません)。
#pragma once
namespace CounterApp {
namespace cx {
namespace data = Windows::UI::Xaml::Data;
}
[Windows::UI::Xaml::Data::Bindable]
public ref class CounterViewModel sealed : cx::data::INotifyPropertyChanged {
public:
CounterViewModel();
virtual event cx::data::PropertyChangedEventHandler^ PropertyChanged;
};
}
#include "pch.h"
#include "CounterViewModel.h"
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Data;
namespace CounterApp {
CounterViewModel::CounterViewModel() {
}
}
これを起点にCounterViewModelを作っていきましょう。ViewModelの大きな役割の一つはView(ここではMainPage.xaml)が必要とするデータをプロパティの形で提供することです。CounterAppアプリでViewが必要としているのはもちろんカウント値(つまりCounterLib::get()が返す値)です。こんなコードになるでしょう。
#pragma once
#include "CounterLib/CounterLib.h"
namespace CounterApp {
namespace cx {
namespace data = Windows::UI::Xaml::Data;
}
[Windows::UI::Xaml::Data::Bindable]
public ref class CounterViewModel sealed : cx::data::INotifyPropertyChanged{
public:
CounterViewModel();
virtual event cx::data::PropertyChangedEventHandler^ PropertyChanged;
property int Count { int get(); }
private:
CounterLib::Counter counter_;
};
}
#include "pch.h"
#include "CounterViewModel.h"
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Data;
namespace CounterApp {
CounterViewModel::CounterViewModel() {
}
// property Count
int CounterViewModel::Count::get() { return counter_.get(); }
}
ご覧のとおり、int型のプロパティ:Countがcounter_.get()を返すように仕組んでいます。このプロパティ値がMainPageにあるTextBlockのTextに反映されればいい。以下がMainPage.xaml(の抜粋)です。
<Page .... />
<Page.Resources>
<!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
<x:String x:Key="AppName">C++/CX Windows 8.1 ストアアプリことはじめ</x:String>
<local:CounterViewModel x:Key="ViewModel"/> <!-- [1] -->
</Page.Resources>
<Grid ...
<!-- 新たに追加: ここから -->
<Grid Grid.Row="1" DataContext="{StaticResource ViewModel}"> <!-- [2] -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="1">
<!-- このGridの中にコントロールを配置します -->
<TextBlock Text="{Binding Count}" ... /> <!-- [3] -->
<Button Command="{Binding Increment}" .../>
<Button Command="{Binding Decrement}" .../>
</Grid>
</Grid>
<!-- 新たに追加: ここまで -->
</Grid>
</Page>
キモとなるのは3つ、
- CounterViewModelに名前を付けてPage.Resourcesに追加すること
- Gridの DataContextに1.で追加したCounterViewModelをセットすること
- TextBlockのTextアトリビュートにCountプロパティをバインドすること
これだけです(ついでに2つのButtonのCommandにもなにやらバインドされていますが、これについては後述)。TextBlockにつける名前(x:Name="...")すら省略し、プロパティだけでViewとViewModelとをつないでいます。
MainPageのコードビハインドに手を加えるのは一か所だけ。MainPage.xaml.hに#include「CounterViewModel.h」を追加します。コードビハインドはもはやCounterViewModelを必要としないのでできればやりたくはないのですが、正しいコード生成のための「おまじない」ってことでカンベンしてください。
ビルドするとエディタ上のMainPage.xamlに大きな0が現れるはず。TextBoxからCounterViewModelのCountプロパティを介してCounter::get()までがつながった証です。

