SHOEISHA iD

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

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

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

特集記事

C++/CX: Windows Runtime Componentをつくる

step6:ちょっと気の利いたコンポーネント

 さてこのViewModel、退屈な仕事をしています。カウンタをIncrement/Decrementし、カウント値に変化があったらその旨をViewに通知しているところ:do_inc/do_decです。この仕事はコンポーネント側に任せ、カウント値の変化はイベントで受け取ることにしましょう。ついでにカウント値の取得もGet()メソッドからCountプロパティに改めます。

Counter.h
#pragma once

#include "CounterLib/CounterLib.h"

namespace CounterComponent {

    namespace cx {
        namespace data = Windows::UI::Xaml::Data;
    }

    [Windows::Foundation::Metadata::WebHostHidden]
    public ref class Counter sealed : cx::data::INotifyPropertyChanged {
    public:
        Counter();
        virtual event cx::data::PropertyChangedEventHandler^ PropertyChanged;

        void Increment();
        void Decrement();
        property int Count { int get(); }

    private:
        void RaisePropertyChanged(Platform::String^ propertyName);
        CounterLib::Counter counter_;
    };

}
Counter.cpp
#include "pch.h"
#include "Counter.h"

using namespace Platform;

namespace CounterComponent {

    Counter::Counter() : counter_() {}

    void Counter::Increment() { 
        int curr = counter_.get(); 
        counter_.increment(); 
        if ( curr != counter_.get() ) {
            RaisePropertyChanged(L"Count");
        } 
    }

    void Counter::Decrement() {
        int curr = counter_.get(); 
        counter_.decrement(); 
        if ( curr != counter_.get() ) {
            RaisePropertyChanged(L"Count"); 
        }
    }

    int  Counter::Count::get() { return counter_.get(); }

    void Counter::RaisePropertyChanged(Platform::String^ propertyName) { 
        PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName));
    }
}

 ViewModel側ではカウント値が変化したこと、つまりPropertyChangedイベントを受け取ればよく、イベント・ハンドラで自身のPropertChangedイベントを発行することでその旨をViewに"横流し"します。

CounterViewModel.h
#pragma once

#include "Common/RelayCommand.h"

namespace CounterApp {

    namespace cx {
      namespace xaml  = Windows::UI::Xaml;
      namespace data  = Windows::UI::Xaml::Data;
      namespace input = Windows::UI::Xaml::Input;
    }

    [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(); }

        property cx::input::ICommand^ Increment  { cx::input::ICommand^ get(); }
        property cx::input::ICommand^ Decrement  { cx::input::ICommand^ get(); }

    private:
        void RaisePropertyChanged(Platform::String^ propertyName);

        CounterComponent::Counter counter_;    
    };

}
CounterViewModel.cpp
#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 {

    auto returnsTrue = [](Object^) { return true; };

    CounterViewModel::CounterViewModel() : counter_() {
        // Counterからのイベントを横流しする。
        counter_.PropertyChanged += ref new PropertyChangedEventHandler(
            [this](Object^ sender, PropertyChangedEventArgs^ e) { 
              if ( e->PropertyName == L"Count" ) RaisePropertyChanged(L"Count"); 
            });
    }

    // property Count
    int  CounterViewModel::Count::get() { return counter_.Count; }

    // property Increment/Decrement
    ICommand^ CounterViewModel::Increment::get() {
        return ref new Common::RelayCommand(returnsTrue, [this](Object^) { counter_.Increment(); }); 
    }

    ICommand^ CounterViewModel::Decrement::get() {
        return ref new Common::RelayCommand(returnsTrue, [this](Object^) { counter_.Decrement(); });
    }

    void CounterViewModel::RaisePropertyChanged(String^ propertyName) {
        PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
    }
}

 C#/VB.NETでも同様に:

CounterViewModel.cs
using System.Windows.Input;
using System.ComponentModel;

namespace CounterApp_CS {

    sealed class CounterViewModel : INotifyPropertyChanged {
        public CounterViewModel() {
            counter_ = new CounterComponent.Counter();
            counter_.PropertyChanged += (sender,e) => { 
                    if ( e.PropertyName == "Count" ) {
                        RaisePropertyChanged("Count");
                    }
                };
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public int Count { get { return counter_.Count; } }

        public ICommand Increment  { get { return new Common.RelayCommand( () => { counter_.Increment(); }); } }
        public ICommand Decrement  { get { return new Common.RelayCommand( () => { counter_.Decrement(); }); } }

        private void RaisePropertyChanged(string propertyName) {
            if ( PropertyChanged != null ) {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private CounterComponent.Counter counter_;    
    };
}
CounterViewModel.vb
Imports System.Windows.Input
Imports System.ComponentModel

    NotInheritable Class CounterViewModel
        Implements INotifyPropertyChanged
        Public Sub New()
            counter_ = New CounterComponent.Counter()
            AddHandler counter_.PropertyChanged,
                       Sub(sender, e)
                            If e.PropertyName = "Count" Then
                               RaisePropertyChanged("Count")
                            End If
                       End Sub
        End Sub

        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
        Public ReadOnly Property Count() As Integer
            Get
                Return counter_.Count
            End Get
        End Property

        Public ReadOnly Property Increment() As ICommand
            Get
                Return New Common.RelayCommand(Sub()
                                                   counter_.Increment()
                                               End Sub)
            End Get
        End Property
        Public ReadOnly Property Decrement() As ICommand
            Get
                Return New Common.RelayCommand(Sub()
                                                   counter_.Decrement()
                                               End Sub)
            End Get
        End Property

        Private Sub RaisePropertyChanged(propertyName As String)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub

        Private counter_ As CounterComponent.Counter
    End Class

 ずいぶんスッキリしました。ViewModelはあくまでViewとModelとを繋ぐのが本来のお役目ですから、ここにロジックを盛り込むのは宜しくなさそうです。"何をやるか"だけでなく"誰がやるか"を適切に振り分けるのも設計のキモですね。

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

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

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/7541 2013/12/26 14:00

イベント

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

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

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

メールバックナンバー