C++/WinRTでUWPその5　データバインディングその3 List～コントロールへのバインド

#1. 今回やること
・[前回から](https://qiita.com/lilac0011/items/2c3f1a31fd5a91345647)の続きです。
・xamlでList～やGrid～というコントロールへのバインドできるView Modelの作成です。やることは
①.ViewModelに前回作成したStringViewModelのプロパティーを追加する。
②.IObservableVector型のプロパティーを追加するです。
・①②を追加したViewModelをListViewコントロール等へバインドすると、IObservableVectorの要素を変更するとListViewも自動で変更してくれるというバインド万歳な感じになります。~~（個人的にはwin32/64APIに慣れているのでバインドとか面倒くさいって思いますが・・・）~~

・[XAML アイテム コントロール: C++/WinRT コレクションへのバインド](https://docs.microsoft.com/ja-jp/windows/uwp/cpp-and-winrt-apis/binding-collection)に詳細は書いてあります。


#2. StringVecViewModelの作成
・プロジェクト→モジュールの追加→View Model(C++/WinRT）から「StringVecViewModel」を追加します。

#2-1. StringVecViewModelの変更
・前回作成したStringViewModel.idlをインポートします。
・いつものようにいらないMyPropertyを削除し、IObsevableVectorのプロパティーおよびStringViewModelのプロパティーを追加します。
・StringViewModelプロパティーは名前を変更せずにそのまま使用して下さい。

```xml:StringVecViewModel.idl（変更後）
import "StringViewModel.idl";

namespace bind_1
{

    runtimeclass StringVecViewModel 
    {
        StringVecViewModel();
        StringViewModel StringViewModel{ get; };

        Windows.Foundation.Collections.IObservableVector<StringViewModel> SVModelVec{ get; };
    }
}
```
<br>
#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にしとけと書いてあるのでそれに従っています。

```C++:StringVecViewModel.h（変更後）
#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>
    {
    };
}

```

<br>
#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で追加します。

```C++:StringVecViewModel.cpp（変更後）
#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で記述し、そこへバインドします。
<br>
<br>


#3. MainPageの変更
#3-1. MainPage.idlの変更
・ここではStringVecViewModelをimportし、プロパティーを記述するだけです。

```xml:MainPage.idl（変更後）
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を記述すると思っていただければ・・・

```xml:MainPage.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の時と同じです。

```C++:MainPage.h（変更後）
#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等のメンバが有り大体のことができそうです。

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

```
<br>

・これでリビルド→実行すると、
![4-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/c3175a9b-e5c8-2541-5109-64178c54f9b0.png)
・こんな感じで「list_inc」をクリックする度にlistviewにlist_incが追加されていきます。画像は2回クリックした後の画像です。要素を消したいときはRemoveAtかRemoveAtEndをイベントハンドラーで追加してください。
・これでListViewコントロール等にバインドできるViewModelができました。

今回作成したものも[github](https://github.com/yoshiyoshi-git/bind_1)においておきます。


文字だけなのもあれなので、次は図形の描画をやります。Win2Dを使用するので楽ちんです。

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