View→Model
おつぎはView→Model、MainPage.xamlから手を入れましょう。ふやす/へらすの2つの<Button>要素からClick="~"を削除し、かわりにCommand="{Binding Modify}"を書き加えます。これで各Buttonをクリックしたときの処理がバインド先(=CounterViewModel)のModifyプロパティに紐づけられます。が、これだけじゃふやす/へらすのどちらがクリックされたか判断できないので、CommandParameterアトリビュートにそれぞれ"Increment"/"Decrement"を指定しておきます。
Buttonのイベント・ハンドラがなくなったのでコード・ビハインドはとっても単純、コンストラクタでバインド先を指定するだけ。
// MainPage.xaml.h (step-2)
// MainPage クラスの宣言
//
#pragma once
#include "Common\LayoutAwarePage.h" // 生成されたヘッダーによって要求されます
#include "MainPage.g.h"
#include "CounterViewModel.h"
namespace CounterApp
{
/// <summary>
/// 多くのアプリケーションに共通の特性を指定する基本ページ。
/// </summary>
public ref class MainPage sealed
{
public:
MainPage();
protected:
virtual void LoadState(Platform::Object^ navigationParameter,
Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ pageState) override;
virtual void SaveState(Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ pageState) override;
};
}
// MainPage.xaml.cpp (step-2)
// MainPage クラスの実装
...
MainPage::MainPage()
{
InitializeComponent();
CounterPage->DataContext = ref new CounterViewModel(); // 変更
}
...
そんなわけでCounterViewModelにModifyプロパティを定義せにゃなりません。Modifyプロパティの型はWindows::UI::Xaml::ICommand^とのことなので、CounterViewmodelそれ自身がICommandになっちゃいましょう。そうすればModifyプロパティはthisを返せばいい。
#pragma once
#include "Common/BindableBase.h" // Common::BindableBase
#include "CounterLib/CounterLib.h" // Counterlib::Counter
#include <memory> // std::uniqur_ptr
namespace CounterApp {
[Windows::UI::Xaml::Data::Bindable]
public ref class CounterViewModel sealed : Common::BindableBase, Windows::UI::Xaml::Input::ICommand {
typedef Windows::UI::Xaml::Input::ICommand command_type;
typedef Platform::Object object;
typedef Windows::Foundation::EventHandler<object^> event_type;
public:
CounterViewModel();
// properties
property int Count { int get(); }
property command_type^ Modify { command_type^ get(); }
// ICommand requirements
virtual void Execute(object^ parameter);
virtual bool CanExecute(object^ parameter);
virtual event event_type^ CanExecuteChanged;
private:
void increment();
void decrement();
std::unique_ptr<CounterLib::Counter> counter_;
};
}
Modifyプロパティ、およびICommandの実装で要求されるExecute,CanExecute,CanExecuteChangedを追加しました。
CounterViewModel.cppはさほどに面倒じゃありません。
#include "pch.h"
#include "CounterViewModel.h"
using namespace Platform;
using namespace Windows::UI::Xaml::Input;
namespace CounterApp {
CounterViewModel::CounterViewModel() : counter_(new CounterLib::BoundedCounter(0,9)) {}
// properties
int CounterViewModel::Count::get() { return counter_->get(); }
ICommand^ CounterViewModel::Modify::get() { return this; }
// ICommand implementations
void CounterViewModel::Execute(Object^ arg) {
String^ cmd = dynamic_cast<String^>(arg);
if ( cmd != nullptr ) {
if ( cmd == L"Increment" ) increment();
else
if ( cmd == L"Decrement" ) decrement();
}
}
bool CounterViewModel::CanExecute(Object^ arg) {
String^ cmd = dynamic_cast<String^>(arg);
if ( cmd != nullptr ) {
if ( cmd == L"Increment" ) return counter_->can_increment();
else
if ( cmd == L"Decrement" ) return counter_->can_decrement();
}
return false;
}
void CounterViewModel::increment() {
counter_->increment();
OnPropertyChanged(L"Count");
CanExecuteChanged(this, nullptr);
}
void CounterViewModel::decrement() {
counter_->decrement();
OnPropertyChanged(L"Count");
CanExecuteChanged(this, nullptr);
}
}
Execute,CanExecuteの引数には、Xaml側で指定したCommandParameter値が引き渡されるので、これを頼りに処理を振り分けています。また、increment/decrementではCanExecuteChangedを呼び出しています。これにより「実行の可否が変化した(かも)」ことがViewに伝わり、ViewがCanExecuteを呼び出して得られる実行可否に応じてコントロール(Button)をEnable/Disableにしてくれます。コンストラクタでModelとしてBoundedCounterを設定していますから、カウント値が上限/下限に達するとふやす/へらすボタンがGrayになってくれますね。
最後に
...そんなわけで、2回にわたってC++/CXによるストアアプリの「はじめの一歩」までをご案内しました。ストアアプリ限定ではあるにせよ、XAMLによるリッチなUIとC/C++の優れたライブラリ群が自由に使えるC++/CXは僕のお気に入りになりそうです。

