1. まずは新規作成
・Visual Studio 2022の「新しいプロジェクトの作成」→「空のアプリ、パッケージ化(デスクトップのWinUI 3)」を選んで新規作成してください。
・名前は「PicViewer」としておきます。
2. ①インストールしなくても、起動できるようにする。
2-1. 「.vcxproj」を編集しよう
・基本的にはMSのページの7と8に書かれていることをやります。
・一度VS2022を終了し、「.vcxproj」を適当なテキストエディタで開きます。今回は「PicViewer.vcxproj」をメモ帳で開きます。
・「<PropertyGroup Label="Globals">」タグを探してください。その中からさらに<AppxPackage>タグを探してください。その値は最初「true」になっているので「false」へ変更し、その上に「<WindowsPackageType>None</WindowsPackageType>を追加します。以下に画像で
・ここまで出来たら、一旦念のためリリースモードでビルドし実行してください。
・x64\Releaseの中にある「PicViewer」フォルダを適当なフォルダへコピーし、以下の3ファイル以外はすべて削除。それでも実行できるはずです。
●Microsoft.WindowsAppRuntime.Bootstrap.dll
●PicViewer.exe
●resources.pri
・つまり、この3ファイルさえあればどこに置いても実行可能となります(別のライブラリ等が必要な場合は除く)
・これで①は終了です。後はいらないボタンとプロパティを削除します。
2-2. 一度WinUI3とC++/WinRTの違いを見てみよう。
2-2-1. ファイル構成
・大きなところではC++/WinRTは「MainPage.xaml」だったものが、WinUI3では「MainWindow.xaml」へ変更になっています。
・C++/WinRT
2-2-2. Main.cpp
・UIへアクセスするnamespaceがC++/WinRTは「winrt::Windows~」だったものが、WinUI3では「winrt::Microsoft~」へ変更になっています。違うところでも書いた気がするけど「Microsoft::~」というnamespaceのライブラリもあるので、それらを使用するときはバッティングしてコンパイルエラーとなります。
・そのときは「using namespace winrt;」を削除して「winrt::Microsoft::~」で書いてください。そんなアホなことにならないようにnamespaceがあるはずなんだけどorz
・UI関連以外は「winrt::Windows~」のnamespaceにあるものが多く、UIに渡すときに間違えて「Windows~」のまま渡してコンパイルエラーとなるときもあるのでかなり鬱陶しいです。もっとちゃんと作らないとますます使われ無くなるぞMS!
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"
using namespace winrt;
using namespace Windows::UI::Xaml;
namespace winrt::Test0001::implementation
{
}
#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
using namespace winrt;
using namespace Microsoft::UI::Xaml;
namespace winrt::PicViewer::implementation
{
}
3. ②メニューから「File→Exit」でアプリを終了するか聞いてくるダイアログを表示する。
3-1 メニューバーを配置
・メニューバーをどうやって配置するのか、WinUI 3 Galleryを見て確認してみます(UI関連は参考になるのでwinui3.0を使うのならインストールした方が良いですよ)。
見た感じそんなに難しくなさそうなので、さっくりMainWindowを変更しましょう。
・メニューを追加して「File→Open」と「File→Exit」も追加しました。
・それぞれ、クリックと「Ctrl+O」と「Ctrl+E」のショートカットも作成しています。
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="PicViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PicViewer"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid RequestedTheme="Default" >
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<MenuBar Background="LightGray" BorderBrush="Black" BorderThickness="1" CornerRadius="5">
<MenuBarItem Title="File">
<MenuFlyoutItem Text="Open" Click="Open_Click">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Modifiers="Control" Key="O"/>
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Exit" Click="Exit_Click">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Modifiers="Control" Key="E"/>
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
</MenuBarItem>
</MenuBar>
</Grid>
</Window>
・MainWindow.xaml.hは各メンバ関数の定義と、コンストラクタでタイトルを変更します。変更しないと「WinUI Desktop」ってデフォルトのファイル名のままになってしまいますから。
・Open_Clickはまだ中身を追加しないのでvoidのまま、Exit_ClickはIAsyncActionへと変更します。
・MSのページに書いてありますが、メッセージダイアログやピッカーを使用する場合HWNDを取得する必要があります。このため、getHwnd()も追加しました。
#pragma once
#include "MainWindow.g.h"
namespace winrt::PicViewer::implementation
{
struct MainWindow : MainWindowT<MainWindow>
{
MainWindow()
{
//タイトル名の変更
this->Title(L"PicViewer");
}
//HWND取得関数
HWND getHwnd();
//Openがクリックされたよ!
void Open_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
//Exitがクリックされたよ! -ここは非同期へ変更
winrt::Windows::Foundation::IAsyncAction Exit_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
};
}
namespace winrt::PicViewer::factory_implementation
{
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}
・mainWindow.xaml.cppでgetHwndとExit_Clickの実装を行います。Exitをクリックすると「Yes」「No」「Cancel」の三つのボタンを表示するContentDialogを表示します。
・「Yes」はそのままアプリを終了。
・「No」はMessageDialogを表示して続行。
・「Cancel」は何も表示せず続行。
・各種ダイアログを表示したいだけだったけど、なんでContentDialogとMessageDialogで諸々違うの...
#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
using namespace winrt;
using namespace Microsoft::UI::Xaml;
//::IWindowNativeと::IInitializeWithWindowを使うのにmicrosoft.ui.xaml.window.h、Shobjidl.hが必要
//MessageDialogを使うのにWindows.UI.Popups.hが必要
#include <microsoft.ui.xaml.window.h>
#include <winrt/Windows.UI.Popups.h>
#include <Shobjidl.h>
namespace winrt::PicViewer::implementation
{
}
HWND winrt::PicViewer::implementation::MainWindow::getHwnd()
{
auto windowNative{ this->m_inner.as<::IWindowNative>() };
HWND hWnd{ 0 };
windowNative->get_WindowHandle(&hWnd);
return hWnd;
}
void winrt::PicViewer::implementation::MainWindow::Open_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
}
winrt::Windows::Foundation::IAsyncAction winrt::PicViewer::implementation::MainWindow::Exit_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
//ContentDialogはwinrt::Microsoft...となる。必要なのはXamlRoot
Microsoft::UI::Xaml::Controls::ContentDialog isenddialog{};
isenddialog.XamlRoot(this->Content().XamlRoot());
isenddialog.Title(box_value(L"Do you want to close?"));
isenddialog.PrimaryButtonText(L"Yes");
isenddialog.SecondaryButtonText(L"No");
isenddialog.CloseButtonText(L"Cancel");
auto result = co_await isenddialog.ShowAsync();
if (result == Microsoft::UI::Xaml::Controls::ContentDialogResult::Primary)
{
//アプリ終了
Application::Current().Exit();
}
else if (result == Microsoft::UI::Xaml::Controls::ContentDialogResult::Secondary)
{
//MessageDialogはwinrt::Windows...となる。必要なのはHWND
auto showdialog{ Windows::UI::Popups::MessageDialog(L"Continue without closing this application") };
showdialog.as<::IInitializeWithWindow>()->Initialize(getHwnd());
co_await showdialog.ShowAsync();
}
}