Windows 11 向けデスクトップ アプリケーションで角の丸めを適用する

角の丸めは、Windows 11 Geometry の最も注目すべき機能です。 Windows 11 では、システムは、すべての WinUI アプリやその他のほとんどのアプリを含むすべての受信トレイ アプリのトップレベル ウィンドウコーナーを自動的に丸めます。 ただし、一部の Win32 アプリは、丸められない場合があります。 このトピックでは、システムによって自動的に丸められない場合に、Win32 アプリのメイン ウィンドウの角を丸くする方法について説明します。

Note

設計上、アプリは、最大化、スナップ、仮想マシン (VM) での実行、Azure Virtual Desktop (AVD) セッションでの実行、Windows Defender Application Guard (WDAG) ウィンドウとしての実行では丸めされません。

角が丸い Windows 11 のメモ帳アプリのスクリーンショット。

アプリが丸くならないのはなぜですか?

アプリのメイン ウィンドウが自動丸め処理を受け取らない場合、それは、処理されないようにフレームをカスタマイズしたためです。 アプリは、デスクトップ ウィンドウ マネージャー (DWM) の観点から見た場合、次の 3 つの主要なカテゴリに分類されます。

  1. デフォルトで角が丸いアプリ。

    これには、メモ帳などの完全なシステム指定のフレームやキャプション コントロール (最小/最大/閉じるボタン) を必要とするアプリが含まれます。 また、WS_THICKFRAME と WS_CAPTION のウィンドウ スタイルの設定や、システムが角を角を丸めるのに使用できる 1 ピクセルの非クライアント領域の境界線の指定など、適切な丸めのための十分な情報をシステムに提供するアプリも含まれます。

  2. ポリシーでは丸められないが、丸め "可能な" アプリ。

    通常、このカテゴリのアプリでは、ウィンドウ フレームの大部分をカスタマイズする必要がありますが、Microsoft Office などシステムによって描画される境界線やシャドウも必要です。 アプリがポリシーによって制御されていない場合は、次のいずれかが原因である可能性があります。

    • フレーム スタイル不足
    • 非クライアント領域が空
    • その他のカスタマイズとして、カスタムシャドウ用に使用される子ウィンドウではない追加のウィンドウなどがあります。

    これらのいずれかを変更すると、自動丸め処理が正常に動作しなくなります。 システム ヒューリスティックを使用してできるだけ多くのアプリを丸めようとしましたが、予測できないカスタマイズの組み合わせがいくつかあるため、これらの場合のために手動のオプトイン API を用意しています。 お使いのアプリでこれらの問題に対処するか、次のセクションで説明されているオプトイン API を呼び出した場合、システムによってアプリのウィンドウを自動的に丸めることができます。 ただし、API はシステムに対するヒントであり、カスタマイズによっては丸め処理が保証されないことに注意してください。

  3. オプトイン API が呼び出された場合でも、丸められることがないアプリ。

    これらのアプリにはフレームや境界線がなく、通常、UI は大幅にカスタマイズされています。 アプリで以下のいずれかが実行されている場合、丸めることはできません。

    • ピクセル単位のアルファ レイヤー化
    • ウィンドウ領域

    たとえば、アプリで、カスタムのシャドウ効果を達成するために、ピクセル単位のアルファ レイヤー化を使用してメイン ウィンドウの周囲に透明のピクセルが描画されている場合、ウィンドウは四角形ではなくなるため、システムは丸め処理を実行できません。

角の丸めを選択する方法

アプリがポリシーによって丸められない場合、必要に応じてこれらの API を使用して、アプリで角の丸めが選択されるようにすることができます。 アプリに必要な角の丸めオプションを指定するには、DWM_WINDOW_CORNER_PREFERENCE 列挙の値 (次の表を参照) を DwmSetWindowAttribute 関数に渡します。

列挙値 説明
DWMWCP_DEFAULT ウィンドウの角を丸めるかどうかをシステムに決めさせます。
DWMWCP_DONOTROUND ウィンドウの角を丸めないでください。
DWMWCP_ROUND 必要に応じて、角を丸めます。
DWMWCP_ROUNDSMALL 必要に応じて角を丸め、半径を小さくします。

この列挙値からの適切な値へのポインターが DwmSetWindowAttribute の 3 番目のパラメーターに渡されます。 設定する属性を指定する 2 番目のパラメーターには、DWMWINDOWATTRIBUTE 列挙で定義されている DWMWA_WINDOW_CORNER_PREFERENCE 値を渡します。

C# アプリの場合

DwmSetWindowAttribute はネイティブの Win32 API であり、.NET コードに直接公開されません。 関数を宣言するには、言語の P/Invoke の実装を使用する必要があります (下の例では C# コードを示しています)。 すべての標準の WinForms および WPF アプリは、自動的に丸められますが、ウィンドウ枠をカスタマイズしたり、サードパーティのフレームワークを使用したりする場合は、角の丸めを選択しなければならないことがあります。 詳細については、「例」を参照してください。

次の例は、DwmSetWindowAttribute または DwmGetWindowAttribute を呼び出して、アプリがポリシーによって丸められない場合に、アプリの丸めエクスペリエンスを制御する方法を示しています。

Note

簡潔でわかりやすくするために、これらの例からエラー処理は省かれています。

例 1 - C# - WPF でのアプリのメイン ウィンドウの丸め

この例は、[DllImport] 属性を使用して C# から DwmSetWindowAttribute を呼び出す方法を示しています。 この定義は角の丸めに固有であることに注意してください。DwmSetWindowAttribute 関数は、指定されたフラグに応じて異なるパラメーターを受け取るように設計されているため、これは一般的なシグネチャではありません。 この例には、dwmapi.h ヘッダーファイルからの関連する列挙型のコピーも含まれています。 Win32 API は 3 番目のパラメーターのポインターを受け取るため、関数を呼び出すときに変数のアドレスを渡すことができるように、必ず ref キーワードを使用してください。 これは、MainWindow.xaml.cs の MainWindow クラスで実行できます。

using System.Runtime.InteropServices;
using System.Windows.Interop;

public partial class MainWindow : Window
{
    // The enum flag for DwmSetWindowAttribute's second parameter, which tells the function what attribute to set.
    // Copied from dwmapi.h
    public enum DWMWINDOWATTRIBUTE
    {
        DWMWA_WINDOW_CORNER_PREFERENCE = 33
    }

    // The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function
    // what value of the enum to set.
    // Copied from dwmapi.h
    public enum DWM_WINDOW_CORNER_PREFERENCE
    {
        DWMWCP_DEFAULT      = 0,
        DWMWCP_DONOTROUND   = 1,
        DWMWCP_ROUND        = 2,
        DWMWCP_ROUNDSMALL   = 3
    }

    // Import dwmapi.dll and define DwmSetWindowAttribute in C# corresponding to the native function.
    [DllImport("dwmapi.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
    internal static extern void DwmSetWindowAttribute(IntPtr hwnd,
                                                     DWMWINDOWATTRIBUTE attribute,
                                                     ref DWM_WINDOW_CORNER_PREFERENCE pvAttribute,
                                                     uint cbAttribute);
    // ...
    // Various other definitions
    // ...
}

次に、MainWindow コンストラクターで、InitalizeComponent 呼び出しの後に、WindowInteropHelper クラスの新しいインスタンスを作成して、基になる HWND へのポインターを取得します (ウィンドウ ハンドル)。 EnsureHandle メソッドを使用して、表示される前に、ウィンドウの HWND の作成をシステムに必ず強制します。通常、システムでは、コンストラクターを終了した後にのみ、これが行われるためです。

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));
    
    // ...
    // Perform any other work necessary
    // ...
}

例 2 - C# - WinForms でのアプリのメイン ウィンドウの丸め

WPF と同様、WinForms アプリでは、最初に P/Invoke を使用して dwmapi.dll と DwmSetWindowAttribute 関数シグネチャをインポートする必要があります。 これは、プライマリ Form クラスで行うことができます。

using System;
using System.Runtime.InteropServices;

public partial class Form1 : Form
{
    // The enum flag for DwmSetWindowAttribute's second parameter, which tells the function what attribute to set.
    // Copied from dwmapi.h
    public enum DWMWINDOWATTRIBUTE
    {
        DWMWA_WINDOW_CORNER_PREFERENCE = 33
    }

    // The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function
    // what value of the enum to set.
    // Copied from dwmapi.h
    public enum DWM_WINDOW_CORNER_PREFERENCE
    {
        DWMWCP_DEFAULT      = 0,
        DWMWCP_DONOTROUND   = 1,
        DWMWCP_ROUND        = 2,
        DWMWCP_ROUNDSMALL   = 3
    }

    // Import dwmapi.dll and define DwmSetWindowAttribute in C# corresponding to the native function.
    [DllImport("dwmapi.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
    internal static extern void DwmSetWindowAttribute(IntPtr hwnd,
                                                     DWMWINDOWATTRIBUTE attribute, 
                                                     ref DWM_WINDOW_CORNER_PREFERENCE pvAttribute, 
                                                     uint cbAttribute);
    
    // ...
    // Various other definitions
    // ...
}

DwmSetWindowAttribute の呼び出しは、WPF アプリの場合と同じですが、HWND を取得するためにヘルパー クラスを使用する必要はありません。これは、単に Form のプロパティであるためです。 InitializeComponent の呼び出しの後、Form コンストラクター内からこれを呼び出します。

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));
    
    // ...
    // Perform any other work necessary
    // ...
}

例 3 - C++ でのアプリのメイン ウィンドウの丸め

ネイティブ C++ アプリの場合、ウィンドウ作成後にメッセージ処理関数で DwmSetWindowAttribute を呼び出して、システムにウィンドウの角を丸めるよう指示できます。

LRESULT ExampleWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{
    switch (message)
    {
    // ...
    // Handle various window messages...
    // ...

    case WM_CREATE:
        // ...
        // Perform app resource initialization after window creation
        // ...
        
        if(hWnd)
        {
            DWM_WINDOW_CORNER_PREFERENCE preference = DWMWCP_ROUND;
            DwmSetWindowAttribute(hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(preference));
        }
        break;

    // ...
    // Handle various other window messages...
    // ...
    }

    return 0;
}

例 4 - 半径が小さいメニューの角の丸め - C++

既定では、メニューはポップアップ ウィンドウであり、丸められません。 アプリでカスタム メニューを作成し、他の標準メニューの丸めポリシーに従わせたい場合は、API を呼び出して、既定の丸めポリシーと一致していないように見える場合でも、このウィンドウを丸める必要があることをシステムに知らせることができます。

HWND CreateCustomMenu()
{
    // Call an app-specific helper to make the window, using traditional APIs.
    HWND hWnd = CreateMenuWindowHelper();

    if (hWnd)
    {
        // Make sure we round the window, using the small radius 
        // because menus are auxiliary UI.
        DWM_WINDOW_CORNER_PREFERENCE preference = DWMWCP_ROUNDSMALL;
        DwmSetWindowAttribute(hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(preference));
    }

    return hWnd;
}