6.⑤十字キーで選んだサムネイルで「Enter」キーを押す、もしくはサムネイルをクリックすると新しいウインドウを開き画像を表示する。
6-1. NewPageの作成
6-2. MainWindow.xamlのGridViewへクリックおよびハンドラの追加
6-1.newPageの作成
・新しいウインドウを追加して、画像を表示出来るように設定していきます。
・まずは、「NewPage」という名前で空白のページ(winui3)を追加します。
・いつものようにMyPropertyや余計なボタンがついているので削除します。
・まずはNewPage.xamlから変更していきます。
・画像しか表示させないので、Imageタグのみ追加です。
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="PicViewer.NewPage"
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">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image x:Name="showImage" />
</StackPanel>
</Page>
・NewPage.idlに外部からBitmapImageを表示できるように関数を追加します。
namespace PicViewer
{
[default_interface]
runtimeclass NewPage : Microsoft.UI.Xaml.Controls.Page
{
NewPage();
void SetImageSource(Microsoft.UI.Xaml.Media.Imaging.BitmapImage source);
}
}
・NewPage.xaml.hへNewPage.idlに追加したSetImageSourceの定義を追加。
#pragma once
#include "NewPage.g.h"
namespace winrt::PicViewer::implementation
{
struct NewPage : NewPageT<NewPage>
{
NewPage()
{
}
//これで画像を表示させる
void SetImageSource(winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage const& source);
};
}
namespace winrt::PicViewer::factory_implementation
{
struct NewPage : NewPageT<NewPage, implementation::NewPage>
{
};
}
・NewPage.xaml.cppへSetImageSourceを実装
#include "pch.h"
#include "NewPage.xaml.h"
#if __has_include("NewPage.g.cpp")
#include "NewPage.g.cpp"
#endif
using namespace winrt;
using namespace Microsoft::UI::Xaml;
namespace winrt::PicViewer::implementation
{
void NewPage::SetImageSource(winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage const& source)
{
showImage().Source(source);
return;
}
}
・これで、外部からSetImageSouceを呼び出せばNewPageへ画像を出力することが出来るようになりました。
6-2. MainWindow.xamlのGridViewへクリックおよびハンドラの追加
・NewPage側の準備が出来たので、MainWindow側の準備を進めて、MainWindowのGridViewに表示されたサムネイルをクリックしたら、NewPageへその画像を出力するように変更します。
・まずはMainWindow.xamlの変更から。
・GridViewへItemClickを追加してthumbGrid_ItemClickイベントハンドラを追加します。
<?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" />
<RowDefinition Height="1*"/>
</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>
<GridView ItemsSource="{x:Bind viewModelVec}" Name="thumbGrid" Grid.Row="1" IsItemClickEnabled="True" ItemClick="thumbGrid_ItemClick">
<GridView.ItemTemplate>
<DataTemplate x:Name="ImageTextDataTemplate" x:DataType="local:thumbViewModel">
<StackPanel Height="170" Width="200" Margin="5">
<Image Source="{x:Bind GetThumbImage}" Height="130" Width="190" />
<StackPanel Margin="0,10">
<TextBlock Text="{x:Bind GetImageName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</Window>
・MainWindow.xaml.hではthumbGrid_ItemClick()で非同期関数を呼ぶのでvoidからIAsyncActionへ変更します。
・NewPage操作用メンバの_newPageを追加
#pragma once
#include "MainWindow.g.h"
namespace winrt::PicViewer::implementation
{
struct MainWindow : MainWindowT<MainWindow>
{
MainWindow()
{
//タイトル名の変更
this->Title(L"PicViewer");
//_thumbViewModelVecの初期化
_thumbViewModelVec = winrt::single_threaded_observable_vector<IInspectable>();
}
//HWND取得関数
HWND getHwnd();
//idlで宣言したゲッター
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> viewModelVec() const
{
return _thumbViewModelVec;
}
//Openがクリックされたよ!-ここは非同期へ変更
winrt::Windows::Foundation::IAsyncAction 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);
private:
//thumbViewModelを保存するメンバ
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> _thumbViewModelVec{ nullptr };
//NewPage操作用メンバ
winrt::Microsoft::UI::Xaml::Window _newPage{ nullptr };
public:
winrt::Windows::Foundation::IAsyncAction thumbGrid_ItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e);
};
}
namespace winrt::PicViewer::factory_implementation
{
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}
・MainWindow.xaml.cppでは非同期型にしたthumbGrid_ItemClick関数の実装を行います。
・C++/WinRTでもそうですが、WinUI3のイベントハンドラはIInspectable型のsenderとEventArgst型のeを引数に持ちます(参照ですが)。そしてそれぞれ以下となります。
・senderはイベントハンドラを送出したコントロール。
・eは送出されたメッセージやコントロールで選択されているオブジェクト等。
・このため、thumbGrid_ItemClick関数ではeからクリックされたthumbViewModelを取得しそこから諸々を取得し、設定を行います。newPageが閉じた時に_newPageをnullptrにするようにイベントの設定も行います。これをしないとNewPageを閉じた後にもう一度NewPageへ画像を出力しようとした時にエラーが発生します。
・今回のMainWindow.xaml.cppでは変更のあるヘッダーのインクルード部分とthumbGrid_ItemClick関数の部分のみ載せます。
#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>
//FolderPickerを使用するのにwinrt/Windows.Storage.Pickers.hが必要
#include <winrt/Windows.Storage.Pickers.h>
//Windows::Storage::Search~を使用するのに必要
#include <winrt/Windows.Storage.Search.h>
//thumbViewModel.hも忘れずに
#include"thumbViewModel.h"
//NewPage.xaml.hも新しいウインドウ表示に必要
#include "NewPage.xaml.h"
//中略
winrt::Windows::Foundation::IAsyncAction winrt::PicViewer::implementation::MainWindow::thumbGrid_ItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e)
{
if (_thumbViewModelVec.Size() == 0)
return;
//クリックしたthumbViewModelを取得
auto&& selectitem = e.ClickedItem().as<PicViewer::thumbViewModel>();
//thumbViewModelからファイル名(NewPageのタイトルに使用)を取得
auto&& imagename = selectitem.GetImageName();
//thumbViewModelからBitmapImageを取得
auto&& bitmapimage = winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage();
auto&& imagestream = co_await selectitem.GetImageFile().OpenAsync(Windows::Storage::FileAccessMode::Read);
bitmapimage.SetSource(imagestream);
//初期化されていなければ、初期化
if (!_newPage)
{
_newPage = winrt::Microsoft::UI::Xaml::Window();
_newPage.Content(winrt::PicViewer::NewPage());
}
//NewPageのSetImageSourceを呼び出して、画像をセット
_newPage.Content().as<PicViewer::NewPage>().SetImageSource(bitmapimage);
//タイトルにファイル名を表示
_newPage.Title(imagename);
//ウインドウが閉じた時にNewPageをnullptrにする(イベントの設定)
_newPage.Closed([&](auto&& sender, auto&& e)
{
_newPage = nullptr;
});
//アクティブ化
if (_newPage)
_newPage.Activate();
}
・ここまで出来ると、NewPageに画像を出力するまで出来ます。
7.⑥新しいウインドウで「→」キーを押すと次の画像を表示、「←」キーを押すと前の画像を表示。
7-1. MainWindowへ戻る、進むのイベントの追加と自分自身へのハンドラの追加
7-2. NewPageへ追加
7-1. MainWindowへ戻る、進むのイベントの追加と自分自身へのハンドラの追加
・外部(NewPage)から呼び出すNextItem()とPrevItem()をまずはMainWindow.idlへ追加。
・MainWindow.xaml.hへ上記関数の定義と自分自身へのハンドラの追加。
・MainWindow.xaml.cppへ実装。
・の順で実装していきます。
・MainWindow.idlへNextItem()とPrevItem()を追加します。非同期型で。
import "thumbViewModel.idl";
namespace PicViewer
{
[default_interface]
runtimeclass MainWindow : Microsoft.UI.Xaml.Window
{
MainWindow();
Windows.Foundation.Collections.IObservableVector<IInspectable> viewModelVec{ get; };
Windows.Foundation.IAsyncAction NextItem();
Windows.Foundation.IAsyncAction PrevItem();
}
}
・MainWindow.xaml.hではへ自分自身へのハンドルを追加し、thisを代入。ハンドルを渡す関数を作成。
・NextItemとPrevItemの定義を追加
・cahngeNewPageImage関数(NewPageの画像を変更する関数)の定義を追加
#pragma once
#include "MainWindow.g.h"
namespace winrt::PicViewer::implementation
{
struct MainWindow : MainWindowT<MainWindow>
{
MainWindow()
{
//タイトル名の変更
this->Title(L"PicViewer");
//_thumbViewModelVecの初期化
_thumbViewModelVec = winrt::single_threaded_observable_vector<IInspectable>();
//mainCurrentへ*thisを代入
mainCurrent = *this;
}
//mainCurrentを渡す
static PicViewer::MainWindow GetMainCurrent()
{
return mainCurrent;
}
//_thumbViewModelVecのindexを渡すとNewPageの表示画像を変更する関数
winrt::Windows::Foundation::IAsyncAction changeNewPageImage(unsigned int _thumbViewModelVec_index);
//idlで定義した次の画像を表示するNextItem
winrt::Windows::Foundation::IAsyncAction NextItem();
//idlで定義した前の画像を表示するPrevItem
winrt::Windows::Foundation::IAsyncAction PrevItem();
//HWND取得関数
HWND getHwnd();
//idlで宣言したゲッター
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> viewModelVec() const
{
return _thumbViewModelVec;
}
//Openがクリックされたよ!-ここは非同期へ変更
winrt::Windows::Foundation::IAsyncAction 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);
private:
//自分自身へのハンドル
static PicViewer::MainWindow MainWindow::mainCurrent;
//thumbViewModelを保存するメンバ
Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> _thumbViewModelVec{ nullptr };
//NewPage操作用メンバ
winrt::Microsoft::UI::Xaml::Window _newPage{ nullptr };
public:
winrt::Windows::Foundation::IAsyncAction thumbGrid_ItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e);
};
}
namespace winrt::PicViewer::factory_implementation
{
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}
・MainWindow.xaml.cppではstaticメンバのmainCurrentの初期化。
・changeNewPageImage関数の実装
・NextItem関数とPrevItem関数の実装を行います。
・変更が無いPrevItem関数以降は略で
#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>
//FolderPickerを使用するのにwinrt/Windows.Storage.Pickers.hが必要
#include <winrt/Windows.Storage.Pickers.h>
//Windows::Storage::Search~を使用するのに必要
#include <winrt/Windows.Storage.Search.h>
//thumbViewModel.hも忘れずに
#include"thumbViewModel.h"
//NewPage.xaml.hも新しいウインドウ表示に必要
#include "NewPage.xaml.h"
//staticメンバなので初期化が必要
winrt::PicViewer::MainWindow winrt::PicViewer::implementation::MainWindow::mainCurrent{ nullptr };
namespace winrt::PicViewer::implementation
{
}
winrt::Windows::Foundation::IAsyncAction winrt::PicViewer::implementation::MainWindow::changeNewPageImage(unsigned int _thumbViewModelVec_index)
{
if (_thumbViewModelVec.Size() == 0)
return;
if (_thumbViewModelVec_index >= _thumbViewModelVec.Size())
return;
//現在選択されているViewModelを取得
auto&& imageViewModel = _thumbViewModelVec.GetAt(_thumbViewModelVec_index).as<PicViewer::thumbViewModel>();
//StorageFileと画像名と画像を取得するためStorage::Streamを取得
auto&& imageFile = imageViewModel.GetImageFile();
auto&& imageName = imageFile.Name();
auto&& imageStream = co_await imageFile.OpenAsync(Windows::Storage::FileAccessMode::Read);
//BitmapImageを取得
Microsoft::UI::Xaml::Media::Imaging::BitmapImage bitmapImage{};
bitmapImage.SetSource(imageStream);
//無ければ作る
if (!_newPage)
{
_newPage = winrt::Microsoft::UI::Xaml::Window();
_newPage.Content(winrt::PicViewer::NewPage());
}
//それぞれを変更
_newPage.Content().as<PicViewer::NewPage>().SetImageSource(bitmapImage);
_newPage.Title(imageName);
_newPage.Activate();
}
winrt::Windows::Foundation::IAsyncAction winrt::PicViewer::implementation::MainWindow::NextItem()
{
//GridViewの選ばれているindexを取得
auto selectindex = thumbGrid().SelectedIndex();
//最後のindexだったら0へ、そうで無ければ次のindexをnextindexへ
unsigned int nextindex = (selectindex + 1) % _thumbViewModelVec.Size();
//GrigViewの選択されているサムネイルをnextindexへ変更
thumbGrid().SelectedIndex(nextindex);
//NewPageの画像をnextindexへ変更
co_await changeNewPageImage(nextindex);
}
winrt::Windows::Foundation::IAsyncAction winrt::PicViewer::implementation::MainWindow::PrevItem()
{
//GridViewの選ばれているindexを取得
auto selectindex = thumbGrid().SelectedIndex();
//最初のindexだったら最後へ、そうで無ければ前のindexをprevindexへ
unsigned int previndex = (selectindex == 0) ? _thumbViewModelVec.Size() - 1 : selectindex - 1;
//GrigViewの選択されているサムネイルをprevindexへ変更
thumbGrid().SelectedIndex(previndex);
//NewPageの画像をprevindexへ変更
co_await changeNewPageImage(previndex);
}
//後略
7-2. NewPageへ追加
・NewPage.xamlへキーボード操作の追加
・そのイベントハンドラへ追加を行っていきます。
・NewPage.xamlへキーボード操作の追加ですが、一番最初の「Pageタグ」にKeyDownおよびイベントハンドラを追加すればNewPageのウインドウが選択されていればキーボードの操作を受け付けます。
・mc:Ignorable="d"の後に追加しました
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="PicViewer.NewPage"
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"
KeyDown="Page_KeyDown"
>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image x:Name="showImage" />
</StackPanel>
</Page>
・NewPage.xaml.hにはイベントハンドラの定義が追加されているのでそこは追記する必要無し。
・cppにはMainWindow.xaml.hをインクルードするのを忘れないでください。
・NewPage.xaml.cppのPage_KeyDoen関数に「←キー」「→キー」が押されたときのことを記述します。
#include "pch.h"
#include "NewPage.xaml.h"
#if __has_include("NewPage.g.cpp")
#include "NewPage.g.cpp"
#endif
using namespace winrt;
using namespace Microsoft::UI::Xaml;
//忘れずにインクルード
#include "MainWindow.xaml.h"
namespace winrt::PicViewer::implementation
{
void NewPage::SetImageSource(winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage const& source)
{
showImage().Source(source);
return;
}
}
void winrt::PicViewer::implementation::NewPage::Page_KeyDown(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e)
{
if (e.Key() == winrt::Windows::System::VirtualKey::Right)
{
auto main = winrt::PicViewer::implementation::MainWindow::GetMainCurrent();
main.NextItem();
}
else if (e.Key() == winrt::Windows::System::VirtualKey::Left)
{
auto main = winrt::PicViewer::implementation::MainWindow::GetMainCurrent();
main.PrevItem();
}
}
・これでNewPageが選択されていれば、キーボードの「←キー」と「→キー」で次の画像、前の画像と変更することが出来るようになります。