SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

C++/CX: Windows 8 ストアアプリことはじめ (2)

データ・バインディングのキホン

View→Model

 おつぎはView→Model、MainPage.xamlから手を入れましょう。ふやす/へらすの2つの<Button>要素からClick="~"を削除し、かわりにCommand="{Binding Modify}"を書き加えます。これで各Buttonをクリックしたときの処理がバインド先(=CounterViewModel)のModifyプロパティに紐づけられます。が、これだけじゃふやす/へらすのどちらがクリックされたか判断できないので、CommandParameterアトリビュートにそれぞれ"Increment"/"Decrement"を指定しておきます。

fig-3 CommandParameterアトリビュートに
それぞれIncrement/Decrementを指定してボタンを識別
screen-shot

 Buttonのイベント・ハンドラがなくなったのでコード・ビハインドはとっても単純、コンストラクタでバインド先を指定するだけ。

list-10
// 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;
    };
}
list-11
// 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を返せばいい。

list-12
#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はさほどに面倒じゃありません。

list-13
#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になってくれますね。

fig-4 カウント値が上限/下限に達するとふやす/へらすボタンがGrayに変化
fig-4 カウント値が上限/下限に達するとふやす/へらすボタンがGrayに変化

最後に

 ...そんなわけで、2回にわたってC++/CXによるストアアプリの「はじめの一歩」までをご案内しました。ストアアプリ限定ではあるにせよ、XAMLによるリッチなUIとC/C++の優れたライブラリ群が自由に使えるC++/CXは僕のお気に入りになりそうです。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/7130 2013/05/14 14:10

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー