C++/WinRTでUWPその3の続きです。
#1.変更を通知するには
変更をコントロールへ通知するのに必要なのは以下の3点です。
①. idlファイルに変更を通知するよ、という宣言を入れる。
②. hファイルとcppファイルにPropertyChanged()というプロパティーを実装する。
③. 変更した箇所でデータメンバからPropertyChangedEventArgsを呼び出す。
それぞれ実装していきます。
#2.変更通知箇所の実装
##2-1. StringViewModel.idlファイルの変更
・宣言に「Windows.UI.Xaml.Data.INotifyPropertyChanged」を追加します。
namespace bind_1
{
[Windows.UI.Xaml.Data.Bindable]
runtimeclass StringViewModel
{
StringViewModel();
String DataString;
}
}
namespace bind_1
{
[Windows.UI.Xaml.Data.Bindable]
runtimeclass StringViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
StringViewModel();
String DataString;
}
}
##2-2. StringViewModel.hファイルの変更
・PropertyChangedプロパティーとm_propertyChangeメンバを追加します。
#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>
{
};
}
#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>
{
};
}
##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)を呼んだ回数も表示するようにしているので、その分の蛇足も入ってます。
#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;
}
#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);
}
}
##2-4. MainPage.cppファイルの変更
・前回追加した、button1_Clickというイベントハンドラーにクリックされたことを追加します。ここは変更後だけ。
#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;
}
・これでビルド→実行すると、以下のようになり・・・ってクリック前は変わらないよ? ![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クリックした後です。ここまでやるとバインドのありがたみが少しは出てきますね。
・詳しくはINotifyPropertyChanged.PropertyChanged Eventとか前回から出ているXAML コントロール: C++/WinRT プロパティへのバインドを確認してみてください。
・さて、次はListViewへのバインドです。ListViewって何個も表示できますよね?そこを実装します。