MFCでもWinRTその3.　MFCアプリからWinRTコントロールの表示（Buttonの表示のみ）

#1. 今回やること
・XAML Islandsを利用して、MFCアプリにUWPのボタンを表示させます。イベントハンドラを追加は次回となります。
・[MFC に XAML Islands で UWP のコントロールを追加してみよう](https://blog.okazuki.jp/entry/2019/11/01/171855)と[C++ デスクトップ (Win32) アプリで標準 WinRT XAML コントロールをホストする](https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/host-standard-control-with-xaml-islands-cpp)を参考にしています。ありがとうございます。

・[前回](https://qiita.com/lilac0011/items/4f1a6d4c689430d16330)からさらに以下のことを追加して行きます。
　①XAML IsLandに必要なヘッダのインクルード
　②app.manifestの追加
　③XAML IsLandの初期化
　④XAML IsLandのハンドルの取得～表示までのコードの追加


#2. プロジェクトの作成
・今回は単一のドキュメントでプロジェクトを作成します。名前は「MFCWinRTButton」で
・いらない機能が追加されるので、「高度な機能」のドッキング可能な・・・はすべてチェックを外します。
・前回のように「C++17」へ変更、「/await」を追加します。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/38b418b2-1d44-6a0d-57a9-3cefbe2e159e.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/9366a3b3-c45e-0a05-ef7b-d34e580ea395.png)
・pch.hはXAML関連のヘッダを追加して、以下のようになりました。

```CPP:pch.h（変更後)
#ifndef PCH_H
#define PCH_H

// プリコンパイルするヘッダーをここに追加します
#include "framework.h"

#undef GetCurrentTime
#undef TRY

#pragma comment(lib, "windowsapp")

#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Popups.h>

#include <hstring.h>

#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>

#endif //PCH_H

```

#3. app.manifestの追加
・ソリューションエクスプローラーのプロジェクトを右クリックし、「追加」→「新しい項目」を選びます。
・「Web」→「XMLファイル」を選び名前に「app.manifest」を入力して追加します。
・追加したら、[デスクトップ アプリケーション プロジェクトを作成する](https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/host-standard-control-with-xaml-islands-cpp#create-a-desktop-application-project)のXMLの中をコピペします。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/e4155f2b-2101-ca06-f0a5-f99b4d4c23b1.png)
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/02098c2f-7ea2-9c92-b0e8-e85e3924262e.png)

```XML:app.manifest（変更後)

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- Windows 10 -->
      <maxversiontested Id="10.0.18362.0"/>
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    </application>
  </compatibility>
</assembly>

```

・このapp.manifestが無いと、ビルド時にエラーが出ます。

#4. XAML IsLandの初期化
・MFCWinRTButton.cppのBOOL CMFCWinRTButtonApp::InitInstance()にwinrtの初期化とxamlマネージャーの初期化を追加します。

```CPP:CMFCWinRTButto.cpp（変更後)
//前略

// CMFCWinRTButtonApp の初期化

BOOL CMFCWinRTButtonApp::InitInstance()
{
	// アプリケーション マニフェストが visual スタイルを有効にするために、
	// ComCtl32.dll Version 6 以降の使用を指定する場合は、
	// Windows XP に InitCommonControlsEx() が必要です。さもなければ、ウィンドウ作成はすべて失敗します。
	INITCOMMONCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	// アプリケーションで使用するすべてのコモン コントロール クラスを含めるには、
	// これを設定します。
	InitCtrls.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&InitCtrls);

	//winrtの初期化
	winrt::init_apartment(winrt::apartment_type::single_threaded);
	//xamlマネージャーの初期化
	winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager winxamlmanager = winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread();


	CWinAppEx::InitInstance();

//後略

```

#5. XAML IsLandのハンドルの取得～表示までのコードの追加
・MFCWinRTButtonView.hにDesktopWindowXamlSourceのプライベートメンバを追加します。
・これを経由して色々な操作を行います。

```CPP:MFCAWinRTButtonTest01View.h（変更後)
#pragma once


class CMFCWinRTButtonView : public CView
{
	//XamlSourceの追加
	winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;

protected: // シリアル化からのみ作成します。
	CMFCWinRTButtonView() noexcept;
	DECLARE_DYNCREATE(CMFCWinRTButtonView)

// 属性
public:
	CMFCWinRTButtonDoc* GetDocument() const;

//後略

```

・クラスビューからCMFCWinRTButtonViewを選び、プロパティよりOnCreateを追加します。
・追加したCMFCWinRTButtonView::OnCreate(LPCREATESTRUCT lpCreateStruct)にコードを追加します。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/dd701d08-2756-55e5-724f-f0029ecd0611.png)

```CPP:MFCAWinRTButtonTest01View.cpp（変更後)

int CMFCWinRTButtonView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// ハンドルを取得
	auto interop = desktopSource.as<IDesktopWindowXamlSourceNative>();

	// ウインドウへアタッチ
	winrt::check_hresult(interop->AttachToWindow(this->m_hWnd));

	//XAML用のHWNDを取得
	HWND hWndXamlIsland = nullptr;
	interop->get_WindowHandle(&hWndXamlIsland);

	//ボタンの作成
	winrt::Windows::UI::Xaml::Controls::Button button;
	button.Content(winrt::box_value(winrt::hstring(L"Button")));

	//XAML contentの作成。ここにコントロールを追加して行きます
	winrt::Windows::UI::Xaml::Controls::StackPanel xamlContainer;

	//StackPanelへコントロール（ボタン）を登録して、StackPanelをXamlSourceへ追加
	xamlContainer.Children().Append(button);
	desktopSource.Content(xamlContainer);

	//今回はウインドウの(0,0)～(500,500)の正方形の領域にXAMLを表示しています。
	//画面全体に表示させたい場合はGetWindowRectでウインドウの大きさを取得して,それを指定してください。
	//サイズ変更に対応する場合は、OnSize()を追加して、ハンドルを取得して、StackPanelの大きさを指定してください。
	::SetWindowPos(hWndXamlIsland, NULL, 0, 0, 500, 500, SWP_SHOWWINDOW);
	xamlContainer.UpdateLayout();

	return 0;
}

```

・追加したコードについての説明はコメントで入れておいたので確認してください。
・[MSのページ](https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/host-standard-control-with-xaml-islands-cpp)ではパッケージ化するって書いてあったけど、パッケージ化しなくても表示できてますね。
・実行するとこんな感じです。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/3370941c-f4dd-14fc-79c8-7bdf1afa1c47.png)


・MFCのSDIアプリにUWPのボタンを表示させてみました。案外簡単にMFCアプリもモダン！にできるでしょう？

・[次回](https://qiita.com/lilac0011/items/35f4badac292240dae19)はこのボタンにイベントハンドラを追加します。

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