single.php

C# WinUI 3アプリでImageコントロールの画像をウィンドウにフィットする方法

C# WinUI 3アプリを作っていく途中で、躓いた部分を備忘録的に投稿します。今回はWinUI3プロジェクトでXaml上のImageコントロールで表示される画像のサイズをウィンドウサイズにフィットさせる方法です。

画像の幅と高さを取得できない

C#のWinUI 3プロジェクトでメインウィンドウのXamlにImageコントロールにはSourceプロパティを使って画像を表示します。

Microsoft.UI.Xaml.Controls.Image img = myimage;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.UriSource = new Uri(img.BaseUri, sImagePath);
img.Source = bitmapImage;

ImageコントロールのStretchプロパティの設定に影響されますが既定値(Uniform)の場合には元画像に合わせて拡大・縮小されます。

ここで問題になるのは、BitmapImage クラスにもMicrosoft.UI.Xaml.Controls.Imageクラスにも元画像のWidth(幅)とHeight(高さ)を取得できるプロパティが無いことです。

ImageコントロールにはWidth(幅)とHeight(高さ)がありますがSourceプロパティに画像を設定した段階で表示されているサイズが取得され元画像のサイズを正しく取得できません。

DecodePixelWidthで代用

色々試してみましたが、BitmapImageクラスのDecodePixelWidth(またはDecodePixelHeight)を使って、元画像のサイズを取得せずに画面サイズに合わせて表示させることができました。

具体的には次の処理を追加します。(myimageは、Xaml上のImageコントロールのx:Name属性で設定した名前です)

void ViewImage(String sImagePath)
{
  IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
  Microsoft.UI.WindowId windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
  AppWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);

  Microsoft.UI.Xaml.Controls.Image img = myimage;
  string sFileName = System.IO.Path.GetFileName(sImagePath);
  BitmapImage bitmapImage = new BitmapImage();
  bitmapImage.UriSource = new Uri(img.BaseUri, sImagePath);

  int appHeight = AppWindow.ClientSize.Height;
  img.Height = bitmapImage.DecodePixelHeight = appHeight;
  img.Width = AppWindow.ClientSize.Width;
  img.Source = bitmapImage;
}

DecodePixelWidth と DecodePixelHeight は、いずれかを設定すれば縦横比は自動的に調整してくれるので、DecodePixelHeight のみを設定しています。

通常ディスプレイの場合には、これでウィンドウサイズにImageコントロールが対応して高さ方向に合わせて画像が調整されて表示されます。

縦長ディスプレイに対応

世の中のディスプレイがすべて横長ではないので、縦長に配置したディスプレイの場合でも正しく表示されるように調整します。

具体的には次の処理を追加します。

void ViewImage(String sImagePath)
{
  IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
  Microsoft.UI.WindowId windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
  AppWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);

  Microsoft.UI.Xaml.Controls.Image img = myimage;
  string sFileName = System.IO.Path.GetFileName(sImagePath);
  BitmapImage bitmapImage = new BitmapImage();
  bitmapImage.UriSource = new Uri(img.BaseUri, sImagePath);

  if (AppWindow.ClientSize.Width > AppWindow.ClientSize.Height)
  {
    //横長ウィンドウなので高さ基準で画像を表示
    int appHeight = AppWindow.ClientSize.Height;
    img.Height = bitmapImage.DecodePixelHeight = appHeight;
    img.Width = AppWindow.ClientSize.Width;
  }
  else
  {
    //縦長ウィンドウなので幅基準で画像を表示
    int appWidth = AppWindow.ClientSize.Width;
    img.Width = bitmapImage.DecodePixelWidth = appWidth;
    img.Height = AppWindow.ClientSize.Height;
  }
  img.Source = bitmapImage;
}

AppWindow.ClientSize.Width と AppWindow.ClientSize.Height を比較して、ディスプレイの縦長と横長で処理を分けています。

縦長ディスプレイの場合には幅基準で画像を表示するようにします。

これで拡大率が100%のディスプレイの場合には、これでウィンドウサイズにImageコントロールが対応して高さ方向に合わせて画像が調整されて表示されます。

高解像度ディスプレイに対応

世の中のディスプレイがすべてFHDではないので、Windows11/10の画面の拡大率に合わせて正しく表示されるように調整します。

具体的には次の処理を追加します。

まずは、GetDpiForWindowを使って、ウィンドウが表示されている画面のDPIを取得します。

IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
double dpi = GetDpiForWindow(hWnd);

次に取得できたDPIを96(拡大率100%の場合のDPI)で割って拡大率を取得します。

拡大率が150%の場合、GetDpiForWindowからは144が取得されます。取得された拡大率を使って、画像の表示サイズを調整します。

img.Height = bitmapImage.DecodePixelHeight = (int)(AppWindow.ClientSize.Height / dpiRasio);;

コード全体では、こんな感じになります。

void ViewImage(String sImagePath)
{
  IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
  Microsoft.UI.WindowId windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
  AppWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);

  double dpiRasio = GetDpiForWindow(hWnd);

  Microsoft.UI.Xaml.Controls.Image img = myimage;
  string sFileName = System.IO.Path.GetFileName(sImagePath);
  BitmapImage bitmapImage = new BitmapImage();
  bitmapImage.UriSource = new Uri(img.BaseUri, sImagePath);

  if (AppWindow.ClientSize.Width > AppWindow.ClientSize.Height)
  {
    //横長ウィンドウなので高さ基準で画像を表示
    int appHeight = (int)(AppWindow.ClientSize.Height / dpiRasio);
    img.Height = bitmapImage.DecodePixelHeight = appHeight;
    img.Width = AppWindow.ClientSize.Width;
  }
  else
  {
    //縦長ウィンドウなので幅基準で画像を表示
    int appWidth = (int)(AppWindow.ClientSize.Width / dpiRasio);
    img.Width = bitmapImage.DecodePixelWidth = appWidth;
    img.Height = AppWindow.ClientSize.Height;
  }
  img.Source = bitmapImage;
}

少しややこしいですが、拡大率はC#で利用する各種アセンブリ内のメソッドやプロパティでは調整してくれないので、対応させるためにはアプリ側で計算する必要があります。

これでディスプレイのオリエンテーションや解像度、拡大率に合わせて、ウィンドウサイズにImageコントロールが対応して高さ方向に合わせて画像が調整されて表示されます。

まとめ

今回は、WinUI 3 アプリのImageコントロールに表示される画像をウィンドウサイズに合わせて拡大・縮小させる方法について書きました。

ImageコントロールのSourceプロパティに画像を設定することで内容が表示されますが、ウィンドウのサイズにあわせて表示画像を調整する場合には、少し手間を加える必要があります。

また、画面のオリエンテーション(縦長、横長)や拡大率に合わせて調整する必要もあります。

C#のWinUI 3アプリでImageコントロールに表示された画像をウィンドウに合わせて調整したい場合の参考になれば幸いです。

スポンサーリンク

最後までご覧いただき、ありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です