#1. 今回やること
・前回からの続きです。
・xamlでList~やGrid~というコントロールへのバインドできるView Modelの作成です。やることは
①.ViewModelに前回作成したStringViewModelのプロパティーを追加する。
②.IObservableVector型のプロパティーを追加するです。
・①②を追加したViewModelをListViewコントロール等へバインドすると、IObservableVectorの要素を変更するとListViewも自動で変更してくれるというバインド万歳な感じになります。(個人的にはwin32/64APIに慣れているのでバインドとか面倒くさいって思いますが・・・)
・XAML アイテム コントロール: C++/WinRT コレクションへのバインドに詳細は書いてあります。
#2. StringVecViewModelの作成
・プロジェクト→モジュールの追加→View Model(C++/WinRT)から「StringVecViewModel」を追加します。
#2-1. StringVecViewModelの変更
・前回作成したStringViewModel.idlをインポートします。
・いつものようにいらないMyPropertyを削除し、IObsevableVectorのプロパティーおよびStringViewModelのプロパティーを追加します。
・StringViewModelプロパティーは名前を変更せずにそのまま使用して下さい。
import "StringViewModel.idl";
namespace bind_1
{
runtimeclass StringVecViewModel
{
StringVecViewModel();
StringViewModel StringViewModel{ get; };
Windows.Foundation.Collections.IObservableVector<StringViewModel> SVModelVec{ get; };
}
}
#2-2. StringVecViewModel.hの変更 ・ヘッダーも同じように変更します。 ・メンバのmSVModelがnullptrなのはMSの[C++/WinRT での API の使用](https://docs.microsoft.com/ja-jp/windows/uwp/cpp-and-winrt-apis/consume-apis)の通りです。コンストラクタで初期化する時はnullptrにしとけと書いてあるのでそれに従っています。
#pragma once
#include "StringVecViewModel.g.h"
#include "StringViewModel.h"
namespace winrt::bind_1::implementation
{
struct StringVecViewModel : StringVecViewModelT<StringVecViewModel>
{
StringVecViewModel();
bind_1::StringViewModel StringViewModel();
Windows::Foundation::Collections::IObservableVector<bind_1::StringViewModel> SVModelVec();
private:
bind_1::StringViewModel m_SVModel{ nullptr };
Windows::Foundation::Collections::IObservableVector<bind_1::StringViewModel> m_SVModelVec{ nullptr };
};
}
namespace winrt::bind_1::factory_implementation
{
struct StringVecViewModel : StringVecViewModelT<StringVecViewModel, implementation::StringVecViewModel>
{
};
}
#2-3. StringVecViewModel.cppの変更 ・内容を実装します。 ・コンストラクタでm_SVModelをmakeするのはMainPageと同じですね。m_SVModelVecはwinrt::single_threaded_observable_vector関数テンプレートでオブジェクトを追加します。詳しくは[C++/WinRT でのコレクション](https://docs.microsoft.com/ja-jp/windows/uwp/cpp-and-winrt-apis/collections)を参照して下さい。 ・m_SVModelVecへはAppendで追加します。
#include "pch.h"
#include "StringVecViewModel.h"
#if __has_include("StringVecViewModel.g.cpp")
#include "StringVecViewModel.g.cpp"
#endif
namespace winrt::bind_1::implementation
{
StringVecViewModel::StringVecViewModel()
{
m_SVModel = winrt::make<bind_1::implementation::StringViewModel>();
m_SVModelVec = winrt::single_threaded_observable_vector<bind_1::StringViewModel>();
m_SVModelVec.Append(m_SVModel);
}
bind_1::StringViewModel StringVecViewModel::StringViewModel()
{
return m_SVModel;
}
Windows::Foundation::Collections::IObservableVector<bind_1::StringViewModel> StringVecViewModel::SVModelVec()
{
return m_SVModelVec;
}
}
・ここまででListViewコントロール等へバインド可能なViewModelの作成が終了しました。次にこれをバインドさせるListViewをxamlで記述し、そこへバインドします。
#3. MainPageの変更
#3-1. MainPage.idlの変更
・ここではStringVecViewModelをimportし、プロパティーを記述するだけです。
import "StringViewModel.idl";
import "StringVecViewModel.idl";
namespace bind_1
{
[default_interface]
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
MainPage();
Int32 MyProperty;
StringViewModel SViewModel{ get; };
StringVecViewModel SVecViewModel{ get; };
}
}
#3-2. MainPage.xamlの変更
・今回はボタン(list_button)一つとListView(listview1)一つを追加します。前回作成したテキストブロックの高さは100へ変更します。
・ついでにlist_buttonのイベントハンドラも追加しておきましょう。
・<ListView>を使用するときはこんな感じでxamlを記述すると思っていただければ・・・
<Page
x:Class="bind_1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:bind_1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="button1" Height="30" Width="300" Content="click" Click="button1_Click"></Button>
<TextBlock x:Name="text1" Text="{x:Bind Path=SViewModel.DataString, Mode=OneWay}" Height="100" Width="300" Margin="10,10,10,10" ></TextBlock>
<Button x:Name="list_buttton" Height="30" Width="300" Content="list_inc" Click="list_buttton_Click" Margin="10,10,10,10"></Button>
<ListView x:Name="listview1" Background="Aqua" Margin="10,10,10,10" Height="300" ItemsSource="{x:Bind SVecViewModel.SVModelVec}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:StringViewModel">
<TextBlock Text="{x:Bind DataString, Mode=OneWay}"></TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
</StackPanel>
</Page>
#3-3. MainPage.hの変更
・list_botton_Click()のイベントハンドラーができていますね。
・ここではStringVecViewModel.hをincludeして、プロパティーと値を保持するメンバを追加するだけです。
・やることはStringViewModelの時と同じです。
#pragma once
#include "MainPage.g.h"
#include "StringViewModel.h"
#include "StringVecViewModel.h"
namespace winrt::bind_1::implementation
{
struct MainPage : MainPageT<MainPage>
{
MainPage();
int32_t MyProperty();
void MyProperty(int32_t value);
void button1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
bind_1::StringViewModel SViewModel();
bind_1::StringVecViewModel SVecViewModel();
private :
bind_1::StringViewModel m_SViewModel{ nullptr };
bind_1::StringVecViewModel m_SVecViewModel{ nullptr };
public:
void list_buttton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
};
}
namespace winrt::bind_1::factory_implementation
{
struct MainPage : MainPageT<MainPage, implementation::MainPage>
{
};
}
#3-4. MainPage.cppの変更
・こっちもやることはStringViewModelの時と同じです。さくっと変更しましょう
・list_button_ClickのイベントハンドラではAppend()関数を呼び出し、IObservableVectorへ要素をを追加します。C++のvectorでいうpush_backですね。そのほかにもClear(要素をすべて削除)、IndexOf、InsertAt、RemoveAt、RemoveAtEnd等のメンバが有り大体のことができそうです。
#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>();
m_SVecViewModel = winrt::make<bind_1::implementation::StringVecViewModel>();
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)
{
SViewModel().DataString(L"Clicked");
}
bind_1::StringViewModel winrt::bind_1::implementation::MainPage::SViewModel()
{
return m_SViewModel;
}
bind_1::StringVecViewModel winrt::bind_1::implementation::MainPage::SVecViewModel()
{
return m_SVecViewModel;
}
void winrt::bind_1::implementation::MainPage::list_buttton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
SVecViewModel().SVModelVec().Append(winrt::make<winrt::bind_1::implementation::StringViewModel>(L"list_inc"));
}
・これでリビルド→実行すると、
・こんな感じで「list_inc」をクリックする度にlistviewにlist_incが追加されていきます。画像は2回クリックした後の画像です。要素を消したいときはRemoveAtかRemoveAtEndをイベントハンドラーで追加してください。
・これでListViewコントロール等にバインドできるViewModelができました。
今回作成したものもgithubにおいておきます。
文字だけなのもあれなので、次は図形の描画をやります。Win2Dを使用するので楽ちんです。