Windows 11でのウィンドウの形状とコントロールの形状 #2

Windows 11では、ウィンドウの形状が変更されます。具体的には、ウィンドウの四角が丸みを持つようになります。

前回の投稿では、このウィンドウの形状変更について、Windows 11 ビルド22000.160を使って確認してみました。今回は、アプリから制御する方法について確認します。

Windows 11のウィンドウの形状変更 (四角の丸み)

Windows 11では、各種ウィンドウやコントロールの四角が丸みを帯びています。

以下のWindows 11 ビルド22000.160のスタート画面のキャプチャーを見てもすぐにわかると思います。

Windows 11 スタート画面(ビルド22000.160)

ウィンドウの四角が丸みを帯びているのはすぐにわかると思います。また、内部のコントロール(上部にある検索用のエディットボックス)、上部右側の「すべてのアプリ」ボタン、上部左側のMicrosoft Edgeの選択枠など)の四角も丸みを帯びています。

このウィンドウやコントロールのWindows 11での形状変更については、マイクロソフトのサイトのWindows 11のデザインガイドに記載があります。

Geometry in Windows 11 - Windows apps | Microsoft Docs (英語)

この中でデザインの例として以下の画像ががげられています。これは、デスクトップアプリの例というよりは、UWPアプリなどの例ですね。

Geometry in Windows 11 (マイクロソフトのサイトより引用)

ただ、デスクトップアプリにおいてもウィンドウやコントロールは四角が丸みを帯びます。

四角を丸める条件と量

このマイクロソフトのサイトには、丸みを持たせる部分の、条件と量(半径)が記載されています。マイクロソフトのサイトから情報を以下に抜粋します。

コーナー半径 (Corner radius)使い方 (Usage)
8pxアプリのウィンドウ、フライアウト、ダイアログなどのトップレベルのコンテナは、8pxのコーナー半径を使用して丸められます。
Top-level containers such as app windows, flyouts and dialogs are rounded using an 8px corner radius.
4pxボタンやリストのバックプレートなどのページ内要素は、4pxのコーナー半径を使用して丸められます。
In-page elements such as buttons and list backplates are rounded using a 4px corner radius.
0px他の直線エッジと交差する直線エッジは丸められません。
Straight edges that intersect with other straight edges are not rounded.
0pxウィンドウをスナップしたり最大化したりすると、ウィンドウの角が丸められません。
Window corners are not rounded when windows are snapped or maximized.
Windows 11における角の3段階の丸め方 (マイクロソフトのサイトより引用)

一つ目の「トップレベルウィンドウの8pxのコーナー半径」とは、下記の絵の左上の赤丸に相当します。二つ目の「ページ内要素の4pxのコーナー半径」とは、下記の絵の右下の赤丸に相当します。三つ目の「他の直線エッジと交差する直線エッジの0pxのコーナー半径」とは、下記の絵の左下の赤丸に相当します。

Rounded corners (マイクロソフトのサイトより引用)

なお、ここでの8pxや4pxは、WPFなどで使用されれている96DPIを基準(100%)とした論理ピクセルです。そのため、DPIスケール率が100% (1.0)のときには、物理ピクセルと1対1で対応します。最近のノートPCやデスクトップPCなどは、DPIスケール率が125%,150%など100%より大きいので、8pxは物理ピクセルでは、10ピクセル(125%のとき)や12ピクセル(150%のとき)に相当します。

システムによるデスクトップアプリへの形状変更の自動適用の強制と除外

標準的な方法で作成されたデスクトップアプリの場合、これらの四角の丸みは、多くの場合に自動的に適用されます。

しかし、一部のデスクトップアプリでは適用されないことがあります。

また、ウィンドウ四角まで情報を表示しているデスクトップアプリのウィンドウなどでは、自動的に適用されることにより困る場合もあります。

このような場合、アプリがAPIを呼び出して、明示的に四角の丸みを適用することをシステムに宣言したり、逆に、自動的に適用されることから除外をシステムに宣言したりできます。

四角の丸みの形状変更を操作するAPI

四角の丸みの形状変更の適用および除外を、アプリから明示的に宣言するには、Windows Vistaで導入されたDesktop Window Manager (DWM) のAPIであるDwmSetWindowAttribute()を利用します。

DWMAPI DwmSetWindowAttribute(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);

このAPIを使用して、ウィンドウを作成した後に、ウィンドウの四角の丸みに関する設定をします。

dwAttributeに設定する値は、DWMWINDOWATTRIBUTE列挙値のDWMWA_WINDOW_CORNER_PREFERENCEを使います。

このDWMWA_WINDOW_CORNER_PREFERENCEは、ビルド22000のWindows SDK (Insider Preview版)では以下のように定義されています。

enum DWMWINDOWATTRIBUTE
{
   ...
    DWMWA_WINDOW_CORNER_PREFERENCE = 33,        // [set] WINDOW_CORNER_PREFERENCE, Controls the policy that rounds top-level window corners
   ...
};

DWMWA_WINDOW_CORNER_PREFERENCEの設定値としては、pvAttributeのバッファに、DWM_WINDOW_CORNER_PREFERENCE列挙値のなかの一つを設定します。ビルド22000のWindows SDK (Insider Preview版)でDWM_WINDOW_CORNER_PREFERENCE列挙値の定義は以下の通りです。

typedef enum {
    // Let the system decide whether or not to round window corners
    DWMWCP_DEFAULT    = 0,
    // Never round window corners
    DWMWCP_DONOTROUND = 1,
    // Round the corners if appropriate
    DWMWCP_ROUND      = 2,
    // Round the corners if appropriate, with a small radius
    DWMWCP_ROUNDSMALL = 3
} DWM_WINDOW_CORNER_PREFERENCE;

四角の丸みの形状変更の適用の強制

四角の丸みの形状変更の適用を強制するには、DWM_WINDOW_CORNER_PREFERENCE列挙値のDWMWCP_ROUNDを設定します。

四角の丸みの形状変更の適用の除外

四角の丸みの形状変更の適用を拒否するには、DWM_WINDOW_CORNER_PREFERENCE列挙値のDWMWCP_DONOTROUNDを設定します。

マネージドアプリからの設定

Desktop Window Manager (DWM)のAPIは、Native APIです。WPFやWinFormなどを使う場合は、多くの場合マネージドアプリ(C#などのアプリ)となります。マネージドアプリからNative APIを呼び出すことになります。

そのため、C#からNative APIを呼び出すために以下のような定義が必要になります。

using System.Runtime.InteropServices;
...
public enum DWMWINDOWATTRIBUTE
{
    DWMWA_WINDOW_CORNER_PREFERENCE = 33
}

public enum DWM_WINDOW_CORNER_PREFERENCE
{
    DWMWCP_DEFAULT      = 0,
    DWMWCP_DONOTROUND   = 1,
    DWMWCP_ROUND        = 2,
    DWMWCP_ROUNDSMALL   = 3
}

[DllImport("dwmapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int DwmSetWindowAttribute(IntPtr hwnd,
    DWMWINDOWATTRIBUTE attribute,
    ref DWM_WINDOW_CORNER_PREFERENCE pvAttribute,
    uint cbAttribute);

上記の定義をしたうえで、APIを呼び出します。

WPFアプリ

WPFアプリの場合なら、下記の例のようにWindowクラスから派生したウィンドウクラスのコンストラクタのInitializeComponent()のあとに、上記のAPIを呼び出します。

using System.Windows.Interop;
...
public MainWindow()
{
    InitializeComponent();

    IntPtr hWnd = new WindowInteropHelper(GetWindow(this)).EnsureHandle();
    var attribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE;
    var preference = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND;
    DwmSetWindowAttribute(hWnd, attribute, ref preference, sizeof(uint));
    
    // ...
    // その他、必要な作業
    // ...
}

WinFormアプリ

WinFormアプリの場合なら、下記の例のようにFormクラスから派生したフォームクラスのコンストラクタのInitializeComponent()のあとに、上記のAPIを呼び出します。

public Form1()
{
    InitializeComponent();

    var attribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE;
    var preference = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND;
    DwmSetWindowAttribute(this.Handle, attribute, ref preference, sizeof(uint));
    
    // ...
    // その他、必要な作業
    // ...
}

UI Controlsテストアプリ 1.0.1

前回の確認に使用したUIコントロールテストアプリに、Desktop Window Manager (DWM) のDWM_WINDOW_CORNER_PREFERENCEを設定変更する機能を追加しました。Version 1.0.1として公開します。

UIControlsWin32 ウィンドウコーナー設定 (右上のコンボボックスリスト)
UIControlsWpf ウィンドウコーナー設定 (右上のコンボボックスリスト)

1.0.0と1.0.1の間のアプリの画面に大きな違いはなく、右上の領域にDWM_WINDOW_CORNER_PREFERENCEの値を変更するコンボボックスリストを追加しました。選択した値がリアルタイムに反映されます。

次に、二つのテストアプリUIControlsWin32とUIControlsWpfで、Window corner preferenceの設定の違いによる四角の形状を確認します。

UIControlsWin32 (Win32ダイアログリソースでの実装)での動作確認

まずは、Win32ダイアログリソースでの実装のUIControlsWin32テストアプリです。

Win32ダイアログリソースでの実装では、ページ内要素であるボタンやチェックボックスなどのコントロールは、すべて子ウィンドウです。そのため、これらのコントロールはウィンドウハンドル(HWND)を持ちます。

このテストアプリではWindow Corner preferenceの設定をトップレベルウィンドウに対してのみ行います。子ウィンドウに対しては設定ていません。

この実装条件と結果から、このWindow Corner preferenceの設定は、設定したウィンドウハンドルにのみ有効で、子ウィンドウには影響を与えないことが確認できました。

DWMWCP_DEFAULT

DWMWCP_DEFAULTでは、Windows 11向けの形状となります。この値はアプリの実装で値を設定しないときの既定値でもあります。そのため、前回の投稿での確認の通り、ウィンドウの四角が丸められます。

UIControlsWin32 ウィンドウコーナー設定(DWMWCP_DEFAULT: 既定)

まずは、Win32ダイアログリソースでの実装のUIControlsWin32テストアプリです。

DWMWCP_DONOTROUND

DWMWCP_DONOTROUNDでは、Windows 10と同じ形状となります。ウィンドウの四角が丸められません。

このテストアプリではトップレベルウィンドウに対してのみ、Window Corner preferenceの設定をします。そのため子ウィンドウ(ボタンやチェックボックスなど)には、変化がありません。子ウィンドウは丸みを帯びたままとなります。

UIControlsWin32 ウィンドウコーナー設定(DWMWCP_DONOTROUND: 丸めない)

DWMWCP_ROUND

DWMWCP_ROUNDでは、Windows 11向けの形状となります。ウィンドウの四角が丸められます。

UIControlsWin32 ウィンドウコーナー設定(DWMWCP_ROUND: 丸める)

DWMWCP_ROUNDSMALL

DWMWCP_ROUNDSMALLでは、Windows 11向けの形状となります。ウィンドウの四角が丸められます。ただし、丸めの度合いが小さいです。この設定は、ボタンなどダイアログ内のコントロールに対して使用すべき設定のようです。

UIControlsWin32 ウィンドウコーナー設定(DWMWCP_ROUNDSMALL: 丸める(小))

UIControlsTestWpf (WPFでの実装)での動作確認

WPFの実装では、ページ内要素であるボタンやチェックボックスなどのコントロールは、すべてWPFで描画されます。そのため、ページ内要素は、ウィンドウではないのでウィンドウハンドル(HWND)はありません。ページ内要素は、前回同様、今回の設定の影響を受けないことが確認できました。

DWMWCP_DEFAULT

DWMWCP_DEFAULTでは、Windows 11向けの形状となります。この値はアプリの実装で値を設定しないときの既定値でもあります。そのため、前回の投稿での確認の通り、ウィンドウの四角が丸められます。

UIControlsWpf ウィンドウコーナー設定(DWMWCP_DEFAULT: 既定)

DWMWCP_DONOTROUND

DWMWCP_DONOTROUNDでは、Windows 10と同じ形状となります。ウィンドウの四角が丸められません。

UIControlsWpf ウィンドウコーナー設定(DWMWCP_DONOTROUND: 丸めない)

DWMWCP_ROUND

DWMWCP_ROUNDでは、Windows 11向けの形状となります。ウィンドウの四角が丸められます。

UIControlsWpf ウィンドウコーナー設定(DWMWCP_ROUND: 丸める)

DWMWCP_ROUNDSMALL

DWMWCP_ROUNDSMALLでは、Windows 11向けの形状となります。ウィンドウの四角が丸められます。ただし、丸めの度合いが小さいです。この設定は、ボタンなどダイアログ内のコントロールに対して使用すべき設定のようです。

UIControlsWpf ウィンドウコーナー設定(DWMWCP_ROUNDSMALL: 丸める(小))

以上、今回の投稿では、トップレベルウィンドウの四角の丸みの形状変更をアプリケーションから明示的に制御するAPIの動作確認をしました。

コメントを残す