LoginSignup
0
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-03-08

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プロパティーは名前を変更せずにそのまま使用して下さい。

StringVecViewModel.idl(変更後)
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 の使用の通りです。コンストラクタで初期化する時はnullptrにしとけと書いてあるのでそれに従っています。

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>
    {
    };
}


2-3. StringVecViewModel.cppの変更

・内容を実装します。
・コンストラクタでm_SVModelをmakeするのはMainPageと同じですね。m_SVModelVecはwinrt::single_threaded_observable_vector関数テンプレートでオブジェクトを追加します。詳しくはC++/WinRT でのコレクションを参照して下さい。
・m_SVModelVecへはAppendで追加します。

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で記述し、そこへバインドします。



3. MainPageの変更

3-1. MainPage.idlの変更

・ここではStringVecViewModelをimportし、プロパティーを記述するだけです。

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を記述すると思っていただければ・・・

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の時と同じです。

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等のメンバが有り大体のことができそうです。

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


・これでリビルド→実行すると、
4-1.png
・こんな感じで「list_inc」をクリックする度にlistviewにlist_incが追加されていきます。画像は2回クリックした後の画像です。要素を消したいときはRemoveAtかRemoveAtEndをイベントハンドラーで追加してください。
・これでListViewコントロール等にバインドできるViewModelができました。

今回作成したものもgithubにおいておきます。

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

もくじへ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0