デスクトップアプリの高DPI対応 #3 – WPFアプリ

前回の投稿でデスクトップアプリ側の高DPI対応状況の宣言について説明しました。今回はアプリ側の描画の部分の高DPI対応について考えます。デスクトップアプリの中でも.NET Frameworkを使って作成されたUIアプリについて考えます。

.NET Frameworkの高DPI対応

.NET Frameworkのアプリには

  • コンソールアプリ
  • WinFormsアプリ
  • WPFアプリ

の3種類あります。この中で高DPI対応が必要なアプリは、WinFormsアプリとWPFアプリです。

.NET FrameworkのUIフレームワーク

.NET Frameworkで標準で用意されているUIフレームワークは以下の二つがあります。

  • Window Forms (WinForms)
  • Windows Presentation Foundation (WPF)

Windows Forms (WinForms)は、.NET Frameworkの初期バージョンから用意されていたUIフレームワークです。すでに18年くらいの歴史があります。

Windows Presentation Foundation (WPF)は、.NET Framework 3.0 (2006年)で用意されたUIフレームワークです。リリース時期はWindows Vistaのリリースの時期と同じです。このUIフレームワークも14年くらいの歴史があります。

これらのUIフレームワークを利用したアプリを高DPI対応にするにはどうすればよいのでしょうか?

今回はWPFアプリの高DPI対応ついて説明します。WinFormsアプリの高DPI対応については次回に説明します。

WPFの高DPI対応

WPFは初版の.NET Framework 3.0のときから高DPIに対応しています。ただ、WPFがリリースされた時のOSはWindows Vistaです。Windows Vistaのときは、System Monitor方式しか存在していませんでした。そのためWPFの初版ではPer Monitor方式とPer Monitor V2方式には対応していません。

WPFがPer Monitorに対応したのは、.NET Framework 4.6.2です。クライアント領域の高DPIの描画処理はPer Monitorと Per Monitor V2は同じ処理でよいので、Per Monitor V2も.NET Framework 4.6.2で対応となります。

WPFのレイアウトの高DPI対応

WPFではレイアウトの座標指定は論理ピクセル(デバイス非依存ピクセル: DIP, Device independent pixels)を使用します。この論理ピクセルは、物理ピクセルと1対1での対応ではありません。WPFでの論理ピクセルは96DPIが基準となっています。デバイスの解像度や物理サイズに関係なく、1論理ピクセルは1/96インチです。論理ピクセルでの座標指定では、整数だけではなく小数点も扱えます。たとえば、座標指定として100などの整数だけでなく100.5などの少数も可能です。WPF側の最終的なディスプレイデバイスへの描画のときに物理ピクセルにマッピングされます。

WPFの論理ピクセルは物理ピクセルの大きさに依存していません。そのため、WPFアプリを高DPI対応するにあたってレイアウトの座標指定は何も変更する必要はありません。

WPFの画像の高DPI対応

WPFでは高DPI対応において画像は指定された画像を拡大表示するのみです。UWPアプリのXAMLのように、ディスプレイスケール率に応じて使用する画像をUIフレームワーク側で自動的に切り替える処理はありません。ディスプレイスケール率に合わせて使用する画像を切り替えたい場合は、Window.DpiChangedイベントやImage.DpiChangedイベントのタイミングで、アプリ側で適切なサイズの画像に切り替えます。

WPFアプリの高DPI対応の具体的な方法

.NET Framework 4.6.2以降を利用する前提での対処方法です。これより前のバージョンの場合は、ここで記載した内容以外に追加の実装が必要となります。

System Monitor/Per Monitor/Per Monitor V2のそれぞれの方式に対応する例を挙げます。しかし、これから実装・改変するWPFアプリは基本的にPer Monitor V2方式の設定をすればよいと思います。

System Monitor方式

WPFは初版からSystem Monitor方式に対応しています。標準でSystem Monitor方式に対応するためにアプリ側で特別な設定は必要ありません。

画像に関しては、ディスプレイスケール率に合わせて、アプリ側で最適なサイズの画像を設定します。具体的にはアプリ起動(Loadedイベント)のタイミングで、最適なサイズの画像を設定します。 System Monitor方式の場合は、アプリの起動後にそのアプリに対するディスプレイスケール率が変更されることはありません。そのため、アプリ起動後のディスプレイスケール率の変更には対応する必要がありません。

Per Monitor方式

.NET Framework 4.6.2以降であればWPFでPer Monitor方式に対応済みです。しかし、標準では有効化されていません。有効化するためには、アプリケーションマニフェストに以下のような<dipAware>の宣言をします。<supportedOS>タグも忘れず設定します。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
    </windowsSettings>
  </application>
  <compatibility>
    <application>
      <!-- Windows Vista -->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
      <!-- Windows 7 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
      <!-- Windows 8 -->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
      <!-- Windows 8.1 -->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
      <!-- Windows 10 -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    </application>
  </compatibility>
</assembly> 

画像に関してはディスプレイスケール率に合わせて、アプリ側で最適なサイズの画像を設定します。具体的にはアプリ起動(Window.LoadedイベントやImage.Loadedイベント)と変更通知(Window.DpiChangedイベントやImage.DpiChangedイベント)のタイミングで、最適なサイズの画像を設定します。

Per Monitor V2方式

WPFは.NET Framework 4.6.2以降であればPer Monitorは対応済みです。WPFはクライアント領域は自前で描画しています。そのためPer Monitorが対応済みであれば、Per Monitor V2方式にもほぼ対応できています。ただし、WPF内でHWNDコントロールやWinFormsコントロールを使用している場合は、それらのコントロールの高DPI対応のためにはPer Monitor V2方式に正式対応した.NET Framework 4.8以降が必要です。

Per Monitor V2方式には、標準では有効化されていません。有効化するためには、アプリケーションマニフェストに以下のような<dipAware>の宣言をします。<supportedOS>タグも忘れず設定します。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
    </windowsSettings>
  </application>
  <compatibility>
    <application>
      <!-- Windows Vista -->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
      <!-- Windows 7 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
      <!-- Windows 8 -->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
      <!-- Windows 8.1 -->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
      <!-- Windows 10 -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    </application>
  </compatibility> 
</assembly> 

画像に関してはディスプレイスケール率に合わせて、アプリ側で最適なサイズの画像を設定します。具体的にはアプリ起動(Window.LoadedイベントやImage.Loadedイベント)と変更通知(Window.DpiChangedイベントやImage.DpiChangedイベント)のタイミングで、最適なサイズの画像を設定します。

WPFアプリの高DPI対応の動作確認

WPFアプリの高DPI対応の動作確認をするときには一つ注意点があります。アプリ側の高DPI対応が正しくできていても、動作確認のときに想定の動作をしないことがあります。アプリをVisual Studioから起動したときなど、デバッガでアタッチしているときに起きます。

この場合は、デバッガでアタッチせずに起動する(Visual Studioのメニューにある「デバッグ」→「デバッグなしで開始」)か、アプリを単体で起動することにより、本来の動作を確認することができます。Visual Studio上から作成したアプリを起動するときは、注意しましょう。


今回はWPFアプリの高DPI対応について説明しました。次回はWinFormsアプリの高DPI対応について説明したいと思います。


2020年07月11日 投稿内容の更新 (XML名前空間の修正)

Visual Studioでアプリケーションマニフェストファイル(app.manifest)を追加したときに作成されるファイルがXML名前空間名(接頭辞:asmv3)を使わず、デフォルトXML名前空間のみを使った形式でした。

この投稿では、当初、以下のようなasmv3というXML名前空間名(接頭辞)を使用した記述でした。

xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"

しかし、Visual Studioで作成されるアプリケーションマニフェストファイルとの整合性を取るため投稿内でのManifestファイルの記述例をXML名前空間名(asmv3)を使用せず、デフォルト名前空間のみを使った形式に修正しました。

なお、XMLファイルの形式としては、どちらも間違っていないので、以前の記述でも問題なく動作します。

“デスクトップアプリの高DPI対応 #3 – WPFアプリ” への3件の返信

コメントを残す