C++/WinRTでUWPその4　データバインディングその2 コントロールへの変更通知

[C++/WinRTでUWPその3](https://qiita.com/lilac0011/items/4e329d3d30da8e14ab2f)の続きです。

#1.変更を通知するには
変更をコントロールへ通知するのに必要なのは以下の3点です。
①. idlファイルに変更を通知するよ、という宣言を入れる。
②. hファイルとcppファイルにPropertyChanged()というプロパティーを実装する。
③. 変更した箇所でデータメンバからPropertyChangedEventArgsを呼び出す。

それぞれ実装していきます。

#2.変更通知箇所の実装

##2-1. StringViewModel.idlファイルの変更
・宣言に「Windows.UI.Xaml.Data.INotifyPropertyChanged」を追加します。

```xml:StringViewModel.idl（変更前）
namespace bind_1
{
    [Windows.UI.Xaml.Data.Bindable]
    runtimeclass StringViewModel 
    {
        StringViewModel();
        String DataString;
    }
}
```

```xml:StringViewModel.idl（変更後）
namespace bind_1
{
    [Windows.UI.Xaml.Data.Bindable]
    runtimeclass StringViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        StringViewModel();
        String DataString;
    }
}
```
<br>

##2-2. StringViewModel.hファイルの変更
・PropertyChangedプロパティーとm_propertyChangeメンバを追加します。

```C++:StringViewModel.h（変更前）
#pragma once

#include "StringViewModel.g.h"

namespace winrt::bind_1::implementation
{
    struct StringViewModel : StringViewModelT<StringViewModel>
    {
        //ここはdefaultを削除
        StringViewModel();
        //ここ追加
        StringViewModel(const winrt::hstring value);

        //ここ追加　セッターですね
        void DataString(winrt::hstring const& value);
        //こちらはゲッター
        winrt::hstring DataString();

    private:
        //ここ追加
        winrt::hstring m_DataString;
    };
}

namespace winrt::bind_1::factory_implementation
{
    struct StringViewModel : StringViewModelT<StringViewModel, implementation::StringViewModel>
    {
    };
}
```

```C++:StringViewModel.h（変更後）
#pragma once

#include "StringViewModel.g.h"

namespace winrt::bind_1::implementation
{
    struct StringViewModel : StringViewModelT<StringViewModel>
    {
        StringViewModel();

        //ここ追加
        StringViewModel(const winrt::hstring value);

        //ここ追加
        void DataString(winrt::hstring const& value);
        winrt::hstring DataString();

        //ここ追加（View Modelの変更通知）
        winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
        void PropertyChanged(winrt::event_token const& token) noexcept;

    private:
        //ここ追加
        winrt::hstring m_DataString;

        //ここ追加（View Modelの変更通知）
        winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
    };
}

namespace winrt::bind_1::factory_implementation
{
    struct StringViewModel : StringViewModelT<StringViewModel, implementation::StringViewModel>
    {
    };
}
```
<br>
##2-3. StringViewModel.cppファイルの変更
・PropertyChangedを実装して、変更を通知する箇所でm_propertyChangedを呼び出します。
・ゲッターではm_propertyChanged.add(handler)を返すだけ、セッターではm_propertyChanged.remove(token)を設定するだけです。
・そしてコントロールに通知したい箇所にm_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"DataString" })をいれればOK。L"DataString"でないと、DataStringで変更通知されません。一度違う文字列でも試してみて下さい。
・DataString(winrt::hstring const& value)を呼んだ回数も表示するようにしているので、その分の蛇足も入ってます。

```C++:StringViewModel.cpp（変更前）
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"

using namespace winrt;
using namespace Windows::UI::Xaml;

namespace winrt::bind_1::implementation
{
    MainPage::MainPage()
    {
        //ここ追加
        m_SViewModel = winrt::make<bind_1::implementation::StringViewModel>();
        InitializeComponent();
    }

    int32_t MainPage::MyProperty()
    {
        throw hresult_not_implemented();
    }

    void MainPage::MyProperty(int32_t /* value */)
    {
        throw hresult_not_implemented();
    }
}

//ここ追加
bind_1::StringViewModel winrt::bind_1::implementation::MainPage::SViewModel()
{
    return m_SViewModel;
}
```
```C++:StringViewModel.cpp（変更後）
#include "pch.h"
#include "StringViewModel.h"
#if __has_include("StringViewModel.g.cpp")
#include "StringViewModel.g.cpp"
#endif

#include <string>
#include <sstream>
namespace winrt::bind_1::implementation
{
    static int g_Num = 0;

    //ここ以降追加
    StringViewModel::StringViewModel() : m_DataString(L"First String")
    {
    }
    StringViewModel::StringViewModel(const winrt::hstring value)
    {
        m_DataString = value;
    }

    void StringViewModel::DataString(winrt::hstring const& value)
    {
        //何回呼ばれたかも表示（蛇足部分）
        g_Num++;
        std::wstring strvalue;
        std::wostringstream woss;
        woss << g_Num;
        strvalue = value + L" " + woss.str();

        m_DataString = strvalue;

        //ここ追加（View Modelの変更通知）
        m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"DataString" });
    }
    winrt::hstring StringViewModel::DataString()
    {
        return m_DataString;
    }

    //ここ追加（View Modelの変更通知）
    winrt::event_token StringViewModel::PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
    {
        return m_propertyChanged.add(handler);
    }
    //ここ追加（View Modelの変更通知）
    void StringViewModel::PropertyChanged(winrt::event_token const& token) noexcept
    {
        m_propertyChanged.remove(token);
    }
}

```
<br>

##2-4. MainPage.cppファイルの変更
・前回追加した、button1_Clickというイベントハンドラーにクリックされたことを追加します。ここは変更後だけ。

```C++:MainPage.cpp（変更後）
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"

using namespace winrt;
using namespace Windows::UI::Xaml;

namespace winrt::bind_1::implementation
{
    MainPage::MainPage()
    {
        //ここ追加
        m_SViewModel = winrt::make<bind_1::implementation::StringViewModel>();
        InitializeComponent();
    }

    int32_t MainPage::MyProperty()
    {
        throw hresult_not_implemented();
    }

    void MainPage::MyProperty(int32_t /* value */)
    {
        throw hresult_not_implemented();
    }
}


void winrt::bind_1::implementation::MainPage::button1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    //ここ追加（View Modelの変更通知）
    SViewModel().DataString(L"Clicked");
}

//ここ追加
bind_1::StringViewModel winrt::bind_1::implementation::MainPage::SViewModel()
{
    return m_SViewModel;
}

```
<br>
・これでビルド→実行すると、以下のようになり・・・ってクリック前は変わらないよ？
![3-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/c2a867e1-e68a-461c-6674-cbde08c058ec.png)

・クリックすると、void StringViewModel::DataString(winrt::hstring const& value)が呼ばれ、その度にコントロールへの更新通知が呼ばれるのでクリックするたびにTextBlockの文字列が変わります。下のの画像は2クリックした後です。ここまでやるとバインドのありがたみが少しは出てきますね。
![3-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/f7d1f4a0-f2c2-e309-5e52-610ebd614169.png)


・詳しくは[INotifyPropertyChanged.PropertyChanged Event](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.inotifypropertychanged.propertychanged?view=winrt-19041)とか前回から出ている[XAML コントロール: C++/WinRT プロパティへのバインド](https://docs.microsoft.com/ja-jp/windows/uwp/cpp-and-winrt-apis/binding-property)を確認してみてください。

・さて、次は[ListViewへのバインド](https://qiita.com/lilac0011/items/a2ce43b124db111f5d26)です。ListViewって何個も表示できますよね？そこを実装します。

[もくじへ](https://qiita.com/lilac0011/items/f53f7401ea7da31e3a0b)