C++/WinRTでUWPその12　複数ウインドウの表示とUIの更新と　その2.

#1. 今回やること
・[前回](https://qiita.com/lilac0011/items/2588ee093a65fb1ca59e)失敗したMainPageのUI更新をやります。
・以下の2種類の方法で更新します

①グローバル変数を使用する。
②ハンドルを渡してそれを通して更新する。


#2. グローバル変数を使用する
・MainPageにグローバル変数を追加して、それ経由でTextBoxを更新します。

##2-1. MainPage.cppの変更
・グローバル変数はpmain_stringとして、m_mainStringのアドレスを代入します。

```cpp:MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"
#include <winrt/Windows.UI.Popups.h>

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

//グローバル変数追加
winrt::appwin_1::StringViewModel* pmain_string;

namespace winrt::appwin_1::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
        m_mainString = make<appwin_1::implementation::StringViewModel>();

        //pmain_stringにm_mainStringのアドレスを代入
        pmain_string = &m_mainString;
    }

    appwin_1::StringViewModel MainPage::MainString()
    {
        return m_mainString;
    }

    void MainPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e)
    {
        if (e.Parameter() == nullptr)
            return;

        hstring blank1mes = winrt::unbox_value<winrt::hstring>(e.Parameter());
        if (blank1mes == L"")
            return;

        auto propertyValue{ e.Parameter().as < Windows::Foundation::IPropertyValue>() };

        if (propertyValue.Type() == Windows::Foundation::PropertyType::String)
            main_text().Text(blank1mes);
        else
            main_text().Text(L"文字列じゃないよ！");

        //念のためちゃんと届いていることを確認するためのメッセージダイアログ
        Windows::UI::Popups::MessageDialog msg{ blank1mes,L"とどいたよ～～～" };
        msg.ShowAsync();
    }

}

winrt::Windows::Foundation::IAsyncAction winrt::appwin_1::implementation::MainPage::make_blank1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    if (blank1Window == nullptr)
    {
        blank1Window = co_await winrt::Windows::UI::WindowManagement::AppWindow::TryCreateAsync();

        blank1Frame.Navigate(xaml_typename<appwin_1::BlankPage1>());

        winrt::Windows::UI::Xaml::Hosting::ElementCompositionPreview::SetAppWindowContent(blank1Window, blank1Frame);
    }

    co_await blank1Window.TryShowAsync();
    blank1Window.Title(L"Blank1");
}

void winrt::appwin_1::implementation::MainPage::send_blank1_navi1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    blank1Frame.Navigate(xaml_typename<appwin_1::BlankPage1>(), winrt::box_value(L"めいんぺ～～じから　ぶらんく1へ"));
}

void winrt::appwin_1::implementation::MainPage::send_blank1_navi2_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    this->Frame().Navigate(xaml_typename<appwin_1::BlankPage1>(), winrt::box_value(L"めいんぺ～～じから　ぶらんく1へ"));
}

winrt::Windows::Foundation::IAsyncAction winrt::appwin_1::implementation::MainPage::close_blank1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    if (blank1Window == nullptr)
        return;

    co_await blank1Window.CloseAsync();
    blank1Window = nullptr;
}
```

#2-2. MainPage.hの変更
・グローバル変数をextern宣言します。

```cpp:MainPage.h
#pragma once

#include "MainPage.g.h"
#include "StringViewModel.h"

#include "winrt/Windows.UI.WindowManagement.h"
#include "winrt/Windows.UI.Xaml.Hosting.h"
#include "BlankPage1.h"

//extern宣言
extern winrt::appwin_1::StringViewModel* pmain_string;

namespace winrt::appwin_1::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();
        appwin_1::StringViewModel MainString();

        Windows::UI::WindowManagement::AppWindow blank1Window{ nullptr };
        Windows::UI::Xaml::Controls::Frame blank1Frame{};
        void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e);

    private:
        appwin_1::StringViewModel m_mainString{ nullptr };

    public:
        winrt::Windows::Foundation::IAsyncAction make_blank1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
        void send_blank1_navi1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
        void send_blank1_navi2_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
        winrt::Windows::Foundation::IAsyncAction close_blank1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
    };
}

namespace winrt::appwin_1::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}
```

##2-3. BlankPage1.cppの変更
・send_Mainpage_globalのClickのイベントハンドラを追加し、pmain_textから文字列を書き換えます。

```cpp:BlankPage1.cpp
#include "pch.h"
#include "BlankPage1.h"
#if __has_include("BlankPage1.g.cpp")
#include "BlankPage1.g.cpp"
#endif

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

namespace winrt::appwin_1::implementation
{
    BlankPage1::BlankPage1()
    {
        InitializeComponent();
    }

    void BlankPage1::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e)
    {
        //BlankPage1起動時はe.Parameter() == nullptrです。そのときは何もしない。
        if (e.Parameter() == nullptr)
            return;

        auto propertyValue{ e.Parameter().as < Windows::Foundation::IPropertyValue>() };

        if (propertyValue.Type() == Windows::Foundation::PropertyType::String)
            blank1_text().Text(winrt::unbox_value<winrt::hstring>(e.Parameter()));
        else
            blank1_text().Text(L"文字列じゃないよ！");
    }
}


void winrt::appwin_1::implementation::BlankPage1::send_Mainpage_navi_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    mainFrame.Navigate(xaml_typename<appwin_1::MainPage>(), winrt::box_value(L"ぶらんく1からめいんぺ～～～～じへ"));
}


void winrt::appwin_1::implementation::BlankPage1::send_Mainpage_global_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    //グローバル変数によるUI更新
    pmain_string->DataString(L"ぶらんく1からめいんぺ～～～～じへ");
}
```

![12-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/9100b0f6-dfe6-0b11-e8f7-01e4ad4da243.png)
・グローバル変数経由でmain_textの文字列が変更できたのが確認できたと思います。
・これでも立派にUI更新できるのですが、グローバル変数は嫌われているのでもう一つの方法も試してみます。


#3. ハンドルを渡してそれを通して更新する

##3-1. MSのサンプルを確認
・MSのサンプルではMainPageでどんなことをやっているのか、一度見てみます。
・[MainPage.h](https://github.com/microsoft/Windows-universal-samples/blob/main/SharedContent/cppwinrt/MainPage.h)と[MainPage.cpp](https://github.com/microsoft/Windows-universal-samples/blob/main/SharedContent/cppwinrt/MainPage.cpp)を見てみると以下のことをして、ハンドルを渡しているのがわかると思います。それ経由でUIを更新します。

・[MainPage.h](https://github.com/microsoft/Windows-universal-samples/blob/main/SharedContent/cppwinrt/MainPage.h)
　36行目で static SDKTemplate::MainPage current;（privateで自分自身へのハンドルを宣言）
　24行目で static SDKTemplate::MainPage Current() { return current; }（pablicでcurrentを返す)


・[MainPage.cpp](https://github.com/microsoft/Windows-universal-samples/blob/main/SharedContent/cppwinrt/MainPage.cpp)
　27行目で SDKTemplate::MainPage MainPage::current{ nullptr };（staticメンバなので実体を生成）
　36行目で MainPage::current = *this;（コンストラクタでthisを代入）

・後はmain_textを変更するメンバ関数があれば、ハンドル経由で他のウインドウからもmain_textが変更可能になりますね。それでは実装していきます。


##3-2. MainPage.idlの変更
・main_textを変更する「SetMainText」関数の宣言を追加します。これをが無いと他のウインドウから変更するときにメンバ関数が無いと叱られます。

```cpp:MainPage.idl
import "StringViewModel.idl";

namespace appwin_1
{
    [default_interface]
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        StringViewModel MainString{ get; };

        void SetMainText(String data);
    }
}
```

##3-3. MainPage.hの変更
・privateメンバの「maincurrent」とpublicメンバ関数の「GetMainCurrent()」と「SetMainText()」を追加します。

```cpp:MainPage.h
#pragma once

#include "MainPage.g.h"
#include "StringViewModel.h"

#include "winrt/Windows.UI.WindowManagement.h"
#include "winrt/Windows.UI.Xaml.Hosting.h"
#include "BlankPage1.h"

namespace winrt::appwin_1::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();
        appwin_1::StringViewModel MainString();

        Windows::UI::WindowManagement::AppWindow blank1Window{ nullptr };
        Windows::UI::Xaml::Controls::Frame blank1Frame{};
        void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e);

        //GetMainCurrentとSetMainTextの追加
        static appwin_1::MainPage GetMainCurrent() { return maincurrent; }
        void SetMainText(hstring const& data);

    private:
        appwin_1::StringViewModel m_mainString{ nullptr };

        //maincurrentの追加
        static appwin_1::MainPage maincurrent;

    public:
        winrt::Windows::Foundation::IAsyncAction make_blank1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
        void send_blank1_navi1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
        void send_blank1_navi2_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
        winrt::Windows::Foundation::IAsyncAction close_blank1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
    };
}

namespace winrt::appwin_1::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}

```

##3-4. MainPage.cppの変更
・MainPage.hで宣言した諸々を実装します。

```cpp:MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"
#include <winrt/Windows.UI.Popups.h>

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

winrt::appwin_1::StringViewModel* pmain_string;

namespace winrt::appwin_1::implementation
{
    //static変数なので実体を生成。変数名にMainPage::を忘れずに
    appwin_1::MainPage MainPage::maincurrent{ nullptr };

    MainPage::MainPage()
    {
        InitializeComponent();
        m_mainString = make<appwin_1::implementation::StringViewModel>();

        pmain_string = &m_mainString;

        //maincurrentにthisを代入
        maincurrent = *this;
    }

    appwin_1::StringViewModel MainPage::MainString()
    {
        return m_mainString;
    }

    void MainPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e)
    {
        if (e.Parameter() == nullptr)
            return;

        hstring blank1mes = winrt::unbox_value<winrt::hstring>(e.Parameter());
        if (blank1mes == L"")
            return;

        auto propertyValue{ e.Parameter().as < Windows::Foundation::IPropertyValue>() };

        if (propertyValue.Type() == Windows::Foundation::PropertyType::String)
            main_text().Text(blank1mes);
        else
            main_text().Text(L"文字列じゃないよ！");

        Windows::UI::Popups::MessageDialog msg{ blank1mes,L"とどいたよ～～～" };
        msg.ShowAsync();
    }

    //main_textの変更
    void MainPage::SetMainText(hstring const& data)
    {
        main_text().Text(data);
    }

}

winrt::Windows::Foundation::IAsyncAction winrt::appwin_1::implementation::MainPage::make_blank1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    if (blank1Window == nullptr)
    {
        blank1Window = co_await winrt::Windows::UI::WindowManagement::AppWindow::TryCreateAsync();

        blank1Frame.Navigate(xaml_typename<appwin_1::BlankPage1>());

        winrt::Windows::UI::Xaml::Hosting::ElementCompositionPreview::SetAppWindowContent(blank1Window, blank1Frame);
    }

    co_await blank1Window.TryShowAsync();
    blank1Window.Title(L"Blank1");
}

void winrt::appwin_1::implementation::MainPage::send_blank1_navi1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    blank1Frame.Navigate(xaml_typename<appwin_1::BlankPage1>(), winrt::box_value(L"めいんぺ～～じから　ぶらんく1へ"));
}

void winrt::appwin_1::implementation::MainPage::send_blank1_navi2_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    this->Frame().Navigate(xaml_typename<appwin_1::BlankPage1>(), winrt::box_value(L"めいんぺ～～じから　ぶらんく1へ"));
}

winrt::Windows::Foundation::IAsyncAction winrt::appwin_1::implementation::MainPage::close_blank1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    if (blank1Window == nullptr)
        return;

    co_await blank1Window.CloseAsync();
    blank1Window = nullptr;
}

```

##3-5. BlankPage1.cppの変更
・send_main_mem_funcへClickのイベントハンドラを追加し、そこへ追加します。
・MainPageのハンドルを受け取って、SetMainTextを呼び出すだけです。

```cpp:BlankPage.cpp
#include "pch.h"
#include "BlankPage1.h"
#if __has_include("BlankPage1.g.cpp")
#include "BlankPage1.g.cpp"
#endif

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

namespace winrt::appwin_1::implementation
{
    BlankPage1::BlankPage1()
    {
        InitializeComponent();
    }

    void BlankPage1::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs const& e)
    {
        //BlankPage1起動時はe.Parameter() == nullptrです。そのときは何もしない。
        if (e.Parameter() == nullptr)
            return;

        auto propertyValue{ e.Parameter().as < Windows::Foundation::IPropertyValue>() };

        if (propertyValue.Type() == Windows::Foundation::PropertyType::String)
            blank1_text().Text(winrt::unbox_value<winrt::hstring>(e.Parameter()));
        else
            blank1_text().Text(L"文字列じゃないよ！");
    }
}


void winrt::appwin_1::implementation::BlankPage1::send_Mainpage_navi_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    mainFrame.Navigate(xaml_typename<appwin_1::MainPage>(), winrt::box_value(L"ぶらんく1からめいんぺ～～～～じへ"));
}


void winrt::appwin_1::implementation::BlankPage1::send_Mainpage_global_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    pmain_string->DataString(L"ぶらんく1からめいんぺ～～～～じへ");
}


void winrt::appwin_1::implementation::BlankPage1::send_Mainpage_mem_func_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    //ここ追加
    auto mainpage = MainPage::GetMainCurrent();
    mainpage.SetMainText(L"ぶらんく1からめいんぺ～～～～じへ はんどるから！！");
}
```

![12-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/966241b1-a175-b441-c3dc-a07a17432616.png)

・以上、複数ウインドウの作成とUIの更新でした。
・MFCでモードレスダイアログ等を作成するときは、createするときに親ウインドウへのポインタを渡してそれ経由で・・・で良いのですが、UWPだとちょっと一手間かかりますね。これに限らず、ウインドウ間の通信はMFCやWin32/64APIと比較するとUWPは色々面倒な印象があります。

[githubへのリンク](https://github.com/yoshiyoshi-git/appwin_1)はここへ。

[目次へ](https://qiita.com/lilac0011/items/f53f7401ea7da31e3a0b)