タイトル バーのカスタマイズ

Windows にはすべてのウィンドウに既定のタイトル バーが用意されており、アプリの個性に合わせてカスタマイズできます。 既定のタイトル バーは、いくつかの標準コンポーネントと、ウィンドウのドラッグやサイズ変更などのコア機能を備えています。

A Windows app showing the title bar

アプリのタイトル バー、許容されるタイトル バー領域のコンテンツ、推奨される UI パターンをカスタマイズする際のガイダンスについては、タイトル バーのデザインに関する記事を参照してください。

重要

この記事では、WinUI 3 の有無に関係なく、Windows アプリ SDK を使うアプリのタイトル バーをカスタマイズする方法について説明します。 UWP と WinUI 2 を使うアプリについては、UWP のタイトル バーのカスタマイズに関する記事を参照してください。

タイトル バーのコンポーネント

この一覧では、標準タイトル バーのコンポーネントについて説明します。

  • タイトル バーの四角形
  • タイトルのテキスト
  • システム アイコン
  • システム メニュー - アクセスするには、アプリ アイコンをクリックするか、タイトル バーを右クリックします
  • キャプション コントロール
    • 最小化ボタン
    • 最大化/復元ボタン
    • 閉じるボタン

ウィンドウ設定

Windows アプリ SDK のウィンドウ機能は、Win32 HWND モデルに基づく Microsoft.UI.Windowing.AppWindow クラスを通して実行されます。 アプリ内の AppWindow と最上位レベルの HWND の間には 1 対 1 のマッピングがあります。 AppWindow とその関連クラスには、タイトル バーのカスタマイズなど、アプリの最上位レベル ウィンドウのさまざまな側面を管理できる API が用意されています。 Windows が提供する既定のタイトル バーを変更して、UI の残りの部分と調和させたり、アプリのキャンバスをタイトル バー領域に拡張して独自のタイトル バー コンテンツを提供したりすることができます。

WinUI 3 のウィンドウ機能は、Microsoft.UI.Xaml.Window クラス (これも Win32 HWND モデルに基づいています) を通して実行されます。 WinUI 3 を使う XAML アプリの場合、XAML ウィンドウ API を使うと、タイトル バーをカスタマイズできるだけでなく、必要に応じて AppWindow API にアクセスすることができます。

AppWindow を使う方法

AppWindow API は、Windows アプリ SDK がサポートする任意の UI フレームワーク (Win32、WPF、WinForms、または WinUI 3) で使用できます。また、必要な API のみを使って段階的に導入できます。

アプリの UI フレームワークとして WinUI 3 XAML を使う場合は、ウィンドウ API と AppWindow API の両方を使用できます。 Windows アプリ SDK 1.4 以降では、XAML ウィンドウと AppWindow はタイトル バーのカスタマイズに同じ AppWindowTitleBar オブジェクトを使います。 Window.AppWindow プロパティを使って、既存の XAML ウィンドウから AppWindow オブジェクトを取得します。 この AppWindow オブジェクトを使うと、タイトル バーのカスタマイズ API にアクセスできます。 タイトル バーのその他の機能にアクセスするには、次のように、XAML ウィンドウから AppWindow API を使用できます: AppWindow.TitleBar.ForegroundColor = Colors.White;

WinUI 3 1.3 以降を使っていない場合は、相互運用 API を使って AppWindow を取得し、AppWindow API を使ってタイトル バーをカスタマイズします。 相互運用 API の詳細については、「アプリ ウィンドウの管理 - UI フレームワークと HWND 相互運用」と Windowing ギャラリーのサンプルを参照してください。

タイトル バーをカスタマイズできるレベル

タイトル バーに適用できるカスタマイズには 2 つのレベルがあります。既定のタイトル バーに軽微な変更を適用する方法と、アプリのキャンバスをタイトル バー領域に拡張して完全なカスタム コンテンツを提供する方法です。

簡易

タイトル バーの色の変更などの簡単なカスタマイズの場合は、AppWindowTitleBar オブジェクトのプロパティを設定して、タイトル バーの要素に使う色を指定できます。 この場合は、タイトル バーの他の部分 (アプリ タイトルの描画やドラッグ領域の定義など) は引き続きシステムが制御します。

完全

もう 1 つのオプションは、既定のシステム タイトル バーを非表示にし、独自のカスタム コンテンツに置き換えることです。 たとえば、テキスト、検索ボックス、またはカスタム メニューをタイトル バー領域に配置できます。 また、このオプションを使って、マイカなどの素材背景をタイトル バー領域に拡張する必要があります。

全面的なカスタマイズを行う場合は、タイトル バー領域に自分でコンテンツを配置する必要があります。また、ドラッグ領域を独自に定義することもできます。 キャプション コントロール (システムの閉じる、最小化、最大化の各ボタン) は引き続き利用可能であり、システムによって処理されますが、アプリ タイトルなどの要素は該当しません。 これらの要素は、アプリの必要に応じて自分で作成する必要があります。

シンプルなカスタマイズ

タイトル バーのタイトル、色、またはアイコンのみをカスタマイズする場合は、アプリ ウィンドウのタイトル バー オブジェクトのプロパティを設定できます。

Title

既定では、タイトル バーにはアプリの種類がウィンドウ タイトルとして表示されます (たとえば、"WinUI Desktop")。 ウィンドウのタイトルを更新して、アプリのわかりやすい表示名を表示する必要があります。

XAML アプリの表示名は Package.appxmanifest ファイルで設定されています。 この値を取得し、それを使ってこのように Title プロパティを設定できます。

Title = AppInfo.Current.DisplayInfo.DisplayName;

ウィンドウのタイトルを変更するには、次に示すように、Window.Title プロパティを 1 行のテキスト値に設定します。

<Window
    ...
    Title="App title">
    ...
</Window>
public MainWindow()
{
    InitializeComponent();
    Title = "App title";
}

AppWindow API を使ってウィンドウ タイトルを変更するには、次に示すように、AppWindow.Title プロパティを 1 行のテキスト値に設定します。 この例は、相互運用 API を使って AppWindow を取得する方法を示しています。これは、アプリで WinUI 3 1.3 以降を使わない場合に必要です。

using Microsoft.UI;           // Needed for WindowId.
using Microsoft.UI.Windowing; // Needed for AppWindow.
using WinRT.Interop;          // Needed for XAML/HWND interop.

private AppWindow m_AppWindow;

public MainWindow()
{
    this.InitializeComponent();

    m_AppWindow = GetAppWindowForCurrentWindow();
    m_AppWindow.Title = "App title";
}

private AppWindow GetAppWindowForCurrentWindow()
{
    IntPtr hWnd = WindowNative.GetWindowHandle(this);
    WindowId wndId = Win32Interop.GetWindowIdFromWindow(hWnd);
    return AppWindow.GetFromWindowId(wndId);
}

既定のタイトル バーの色をカスタマイズする場合、または既定のウィンドウ アイコンを変更する場合は、AppWindow API を使うか、タイトル バーを完全にカスタマイズする必要があります。

この例は、AppWindowTitleBar のインスタンスを取得し、color プロパティを設定する方法を示しています。

重要

アプリが Windows 10 上で実行される場合、色のカスタマイズは無視されます。

// Assumes "this" is a XAML Window. In projects that don't use 
// WinUI 3 1.3 or later, use interop APIs to get the AppWindow.
AppWindow m_AppWindow = this.AppWindow;

private bool SetTitleBarColors()
{
    // Check to see if customization is supported.
    // The method returns true on Windows 10 since Windows App SDK 1.2,
    // and on all versions of Windows App SDK on Windows 11.
    if (AppWindowTitleBar.IsCustomizationSupported())
    {
        AppWindowTitleBar m_TitleBar = m_AppWindow.TitleBar;

        // Set active window colors.
        // Note: No effect when app is running on Windows 10
        // because color customization is not supported.
        m_TitleBar.ForegroundColor = Colors.White;
        m_TitleBar.BackgroundColor = Colors.Green;
        m_TitleBar.ButtonForegroundColor = Colors.White;
        m_TitleBar.ButtonBackgroundColor = Colors.SeaGreen;
        m_TitleBar.ButtonHoverForegroundColor = Colors.Gainsboro;
        m_TitleBar.ButtonHoverBackgroundColor = Colors.DarkSeaGreen;
        m_TitleBar.ButtonPressedForegroundColor = Colors.Gray;
        m_TitleBar.ButtonPressedBackgroundColor = Colors.LightGreen;

        // Set inactive window colors.
        // Note: No effect when app is running on Windows 10
        // because color customization is not supported.
        m_TitleBar.InactiveForegroundColor = Colors.Gainsboro;
        m_TitleBar.InactiveBackgroundColor = Colors.SeaGreen;
        m_TitleBar.ButtonInactiveForegroundColor = Colors.Gainsboro;
        m_TitleBar.ButtonInactiveBackgroundColor = Colors.SeaGreen;
        return true;
    }
    return false;
}

タイトル バーの色を設定するときには、次の点に注意する必要があります。

  • ボタンの背景色は、閉じるボタンの "ホバー" 時と "押下" 時の状態に適用されません。 それらの状態に対して、閉じるボタンには常にシステム定義の色が使われます。
  • color プロパティを null に設定すると、既定のシステム色にリセットされます。
  • 透明色は設定できません。 色のアルファ チャネルは無視されます。

Windows には、ユーザーが選んだアクセント カラーをタイトル バーに適用するオプションが用意されています。 タイトル バーの色を設定する場合は、すべての色を明示的に設定することをお勧めします。 これにより、ユーザー定義の色設定によって意図しない色の組み合わせが発生することがなくなります。

アイコンとシステム メニュー

システム アイコンを非表示にするか、カスタム アイコンに置き換えることができます。 システム アイコンを右クリックするか 1 回タップすると、システム メニューが表示されます。 ダブル クリックするかタップすると、ウィンドウが閉じます。

システム アイコンとそれに関連する動作を表示または非表示にするには、タイトル バーの IconShowOptions プロパティを設定します。

m_TitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;

カスタム ウィンドウ アイコンを使うには、AppWindow.SetIcon メソッドのいずれかを呼び出して新しいアイコンを設定します。

  • SetIcon(String)

    現在、SetIcon (String) メソッドは .ico ファイルでのみ機能します。 このメソッドに渡す文字列は、.ico ファイルへの完全修飾パスです。

    m_AppWindow.SetIcon("iconPath/iconName.ico");
    
  • SetIcon(IconId)

    CreateIcon などの Icon 関数のいずれかからアイコン (HICON) へのハンドルが既にある場合は、GetIconIdFromIcon 相互運用 API を使って IconId を取得できます。 次に、IconIdSetIcon (IconId) メソッドに渡してウィンドウ アイコンを設定します。

    m_AppWindow.SetIcon(iconId));
    

完全なカスタマイズ

タイトル バーの全面的なカスタマイズを選択すると、アプリのクライアント領域が拡張され、タイトル バーの領域を含めてウィンドウ全体が対象になります。 キャプション ボタン (ウィンドウによって提供されます) を除き、ウィンドウ全体の描画と入力処理を独自に行う必要があります。

システム タイトル バーを非表示にして、コンテンツをタイトル バー領域に拡張するには、アプリのコンテンツをタイトル バー領域に拡張するプロパティを true に設定します。 XAML アプリでは、このプロパティはアプリの OnLaunched メソッド (App.xaml.cs) またはアプリの最初のページで設定できます。

ヒント

すぐにすべてのコードを確認するには、「完全なカスタマイズの例」セクションを参照してください。

この例は、Window.ExtendsContentIntoTitleBar プロパティを true に設定する方法を示しています。

public MainWindow()
{
    this.InitializeComponent();

    // Hide system title bar.
    ExtendsContentIntoTitleBar = true;
}

注意事項

XAML IntelliSense で Window に対して ExtendsContentIntoTitleBar が表示されますが、XAML で設定するとエラーが発生します。 代わりに、コードでこのプロパティを設定します。

この例は、AppWindowTitleBar を取得し、AppWindow.ExtendsContentIntoTitleBar プロパティを true に設定する方法を示しています。 この例は、相互運用 API を使って AppWindow を取得する方法を示しています。これは、アプリで WinUI 3 1.3 以降を使わない場合に必要です。

using Microsoft.UI;           // Needed for WindowId.
using Microsoft.UI.Windowing; // Needed for AppWindow.
using WinRT.Interop;          // Needed for XAML/HWND interop.

private AppWindow m_AppWindow;

public MainWindow()
{
    this.InitializeComponent();

    m_AppWindow = GetAppWindowForCurrentWindow();
    var titleBar = m_AppWindow.TitleBar;
    // Hide system title bar.
    titleBar.ExtendsContentIntoTitleBar = true;
}

private AppWindow GetAppWindowForCurrentWindow()
{
    IntPtr hWnd = WindowNative.GetWindowHandle(this);
    WindowId wndId = Win32Interop.GetWindowIdFromWindow(hWnd);
    return AppWindow.GetFromWindowId(wndId);
}

タイトル バーのコンテンツと既定のドラッグ領域

アプリをタイトル バー領域に拡張する場合は、タイトル バーの UI を定義し、管理する必要があります。 通常、これには少なくともタイトル テキストとドラッグ領域の指定が含まれます。 タイトル バーのドラッグ領域には、ユーザーがクリックとドラッグを使ってウィンドウを移動できる場所を定義します。 これは、ユーザーが右クリックしてシステム メニューを表示できる場所でもあります。

許容されるタイトル バーのコンテンツと推奨される UI パターンの詳細については、タイトル バーのデザインに関する記事を参照してください。

この例は、対話型コンテンツを含まないカスタム タイトル バー UI の XAML を示しています。

<Grid x:Name="AppTitleBar"  
      Height="32">
    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
        <ColumnDefinition/>
        <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
    </Grid.ColumnDefinitions>
    <Image x:Name="TitleBarIcon" Source="ms-appx:///Assets/StoreLogo.png"
           Grid.Column="1"
           HorizontalAlignment="Left"
           Width="16" Height="16"
           Margin="8,0,0,0"/>
    <TextBlock x:Name="TitleBarTextBlock" 
               Text="App title" 
               Style="{StaticResource CaptionTextBlockStyle}"
               Grid.Column="1"
               VerticalAlignment="Center"
               Margin="28,0,0,0"/>
</Grid>

重要

LeftPaddingColumnRightPaddingColumn は、キャプション ボタン用の領域を予約するために使われます。 これらの列の Width 値は、後述するコードで設定します。 コードと説明については、「システム キャプション ボタン」セクションを参照してください。

XAML アプリには、Package.appxmanifest ファイルで設定した表示名があります。 この値を取得して、このようにカスタム タイトル バーで使用できます。

TitleBarTextBlock.Text = AppInfo.Current.DisplayInfo.DisplayName;

コンテンツをタイトル バー領域に拡張すると、システム タイトル バーは非表示になり、システム タイトル バーと同じく、画面の幅全体にキャプション ボタンとドラッグ領域がある既定の AppWindowTitleBar が作成されます。 タイトル バーに対話型コンテンツを配置しない場合は、この既定のドラッグ領域をそのままにしておくことができます。 タイトル バーに対話型コンテンツを配置する場合は、対話型の領域を指定する必要があります。これについては次のセクションで説明します。

注意事項

カスタム ドラッグ領域を定義する場合、既定のタイトル バー領域のウィンドウの上部に配置する必要はありません。UI の任意の部分をドラッグ領域として定義できます。 ただし、ドラッグ領域を別の場所に配置すると、ユーザーが見つけにくくなる可能性があります。

対話型コンテンツ

ボタン、メニュー、検索ボックスなどの対話型コントロールをアプリの上部に配置して、タイトル バーにあるように見せることができます。 ただし、対話型要素がユーザー入力を確実に受け取ると同時に、ユーザーがウィンドウを動かすことができるようにするには、どの領域が対話型であるかを指定する必要があります。

A Windows app with a search box in the title bar

タイトル バー領域に対話型コンテンツを追加する場合は、InputNonClientPointerSource クラスを使って、入力がタイトル バーで処理されるのではなく、対話型コントロールに渡される領域を指定する必要があります。 対話型領域を設定するには、InputNonClientPointerSource.SetRegionRects メソッドを呼び出します。 このメソッドは、設定する領域の種類 (この場合は Passthrough) を指定する値と、四角形の配列 (それぞれが Passthrough 領域を定義します) を受け取ります。 タイトル バーのサイズが変わった場合は、新しいサイズに合わせて対話型領域を再計算し、新しい値で SetRegionRects を呼び出す必要があります。

この例は、検索ボックスと PersonPicture アカウント コントロールを備えたカスタム タイトル バー UI を示しています。 これらのコントロールを表す対話型の四角形を計算し、設定して、入力が渡されるようにする方法を示しています。

このコードについて注意すべき重要な点がいくつかあります。

  • 対話型コンテンツのタイトル バー デザイン ガイダンスに従って、AppTitleBar グリッドの高さを 48 に設定します。
  • キャプション ボタンがタイトル バーと同じ高さになるように、PreferredHeightOptionTall に設定します。
  • コントロールのサイズ変更と領域の計算を簡単にするために、レイアウトに複数の名前付き列と共に Grid を使います。
  • AutoSuggestBox を含む列に MinWidth と共に "スター" (*) サイズ設定を使って、ウィンドウに合わせて自動的にサイズが設定されるようにします。
  • RightDragColumnMinWidth を設定すると、ウィンドウのサイズが変更された場合でも常にドラッグできる小さな領域が予約されます。
  • MainWindow コンストラクターで ExtendsContentIntoTitleBartrue に設定します。 後で呼び出すコードで設定すると、既定のシステム タイトル バーが最初に表示され、その後非表示にすることができます。
  • AppTitleBar 要素が読み込まれた後、最初の呼び出しを行って対話型領域を計算します。 そうしないと、計算に使われる要素が正しい値になる保証がありません。
  • 対話型の四角形の計算は、AppTitleBar 要素のサイズ (AppTitleBar_SizeChanged) が変わった後にのみ更新します。 ウィンドウの Changed イベントに依存している場合、AppTitleBar のサイズが変更される前にイベントが発生し、計算に正しくない値が使われる状況 (ウィンドウの最大化や最小化など) が発生します。
  • ExtendsContentIntoTitleBar を調べてカスタム タイトル バーが使われていることを確認した後にのみ、カスタム ドラッグおよび対話型領域を設定します。
<Grid x:Name="AppTitleBar"
      Height="48">
    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
        <ColumnDefinition x:Name="IconColumn" Width="Auto"/>
        <ColumnDefinition x:Name="TitleColumn" Width="Auto"/>
        <ColumnDefinition x:Name="LeftDragColumn" Width="*"/>
        <ColumnDefinition x:Name="SearchColumn" Width="4*" MinWidth="220"/>
        <ColumnDefinition x:Name="RightDragColumn" Width="*" MinWidth="48"/>
        <ColumnDefinition x:Name="AccountColumn" Width="Auto"/>
        <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
    </Grid.ColumnDefinitions>
    <Image x:Name="TitleBarIcon" 
           Source="ms-appx:///Assets/StoreLogo.png"
           Grid.Column="1"
           Width="16" Height="16"
           Margin="8,0,4,0"/>
    <TextBlock x:Name="TitleBarTextBlock"
               Text="App title" 
               Style="{StaticResource CaptionTextBlockStyle}"
               Grid.Column="2"
               VerticalAlignment="Center">
    </TextBlock>
    <AutoSuggestBox x:Name="TitleBarSearchBox" 
                    Grid.Column="4" 
                    QueryIcon="Find"
                    PlaceholderText="Search"
                    VerticalAlignment="Center"
                    MaxWidth="600"/>
    <PersonPicture x:Name="PersonPic" 
                   Grid.Column="6" 
                   Height="32" Margin="0,0,16,0"/>
</Grid>

このコードは、AutoSuggestBox および PersonPicture コントロールに対応する対話型領域を計算して設定する方法を示しています。

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();

        // Assumes "this" is a XAML Window. In projects that don't use 
        // WinUI 3 1.3 or later, use interop APIs to get the AppWindow.
        m_AppWindow = this.AppWindow;
        AppTitleBar.Loaded += AppTitleBar_Loaded;
        AppTitleBar.SizeChanged += AppTitleBar_SizeChanged;
        ExtendsContentIntoTitleBar = true;
        TitleBarTextBlock.Text = AppInfo.Current.DisplayInfo.DisplayName;
    }

    private void AppTitleBar_Loaded(object sender, RoutedEventArgs e)
    {
        if (ExtendsContentIntoTitleBar == true)
        {
            // Set the initial interactive regions.
            SetRegionsForCustomTitleBar();
        }
    }

    private void AppTitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (ExtendsContentIntoTitleBar == true)
        {
            // Update interactive regions if the size of the window changes.
            SetRegionsForCustomTitleBar();
        }
    }

    private void SetRegionsForCustomTitleBar()
    {
        // Specify the interactive regions of the title bar.

        double scaleAdjustment = AppTitleBar.XamlRoot.RasterizationScale;

        RightPaddingColumn.Width = new GridLength(m_AppWindow.TitleBar.RightInset / scaleAdjustment);
        LeftPaddingColumn.Width = new GridLength(m_AppWindow.TitleBar.LeftInset / scaleAdjustment);

        GeneralTransform transform = TitleBarSearchBox.TransformToVisual(null);
        Rect bounds = transform.TransformBounds(new Rect(0, 0, 
                                                         TitleBarSearchBox.ActualWidth,
                                                         TitleBarSearchBox.ActualHeight));
        Windows.Graphics.RectInt32 SearchBoxRect = GetRect(bounds, scaleAdjustment);
        
        transform = PersonPic.TransformToVisual(null);
        bounds = transform.TransformBounds(new Rect(0, 0,
                                                    PersonPic.ActualWidth,
                                                    PersonPic.ActualHeight));
        Windows.Graphics.RectInt32 PersonPicRect = GetRect(bounds, scaleAdjustment);

        var rectArray = new Windows.Graphics.RectInt32[] { SearchBoxRect, PersonPicRect };

        InputNonClientPointerSource nonClientInputSrc =
            InputNonClientPointerSource.GetForWindowId(this.AppWindow.Id);
        nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, rectArray);
    }

    private Windows.Graphics.RectInt32 GetRect(Rect bounds, double scale)
    {
        return new Windows.Graphics.RectInt32(
            _X: (int)Math.Round(bounds.X * scale),
            _Y: (int)Math.Round(bounds.Y * scale),
            _Width: (int)Math.Round(bounds.Width * scale),
            _Height: (int)Math.Round(bounds.Height * scale)
        );
    }
}

警告

AppWindow は、論理座標を使わない UI フレームワークとの互換性のために物理ピクセルを使います。 WPF または WinUI 3、RightInsetLeftInset を使っていて、表示倍率が 100% でない場合は、領域の計算に使われる値を調整する必要があります。 この例では、表示倍率の設定を考慮して scaleAdjustment 値を取得します。

システム キャプション ボタン

アプリ ウィンドウの左上隅または右上隅は、システム キャプション ボタン (最小化、最大化/元に戻す、閉じる) 用に予約されています。 ウィンドウをドラッグ、最小化、最大化、および閉じるための最低限の機能を確保するために、キャプション ボタン領域の制御は、システムが保持します。 システムにより、左から右に記述する言語の場合は右上隅に、右から左に記述する言語の場合は左上隅に閉じるボタンが描画されます。

キャプション コントロール領域の背面に (たとえば背景として) コンテンツを描画することもできますが、ユーザーが操作するための UI は配置しないでください。 キャプション コントロールの入力はシステムによって処理されるため、入力は受け取りません。

前の例のこれらの行は、タイトル バーを定義する XAML のパディング列を示しています。 余白ではなくパディング列を使うと、キャプション コントロール ボタンの下の領域に確実に背景が描画されます (透明なボタンの場合)。 左右の両方のパディング列を使うと、右から左、左から右のどちらのレイアウトであっても、タイトル バーは正しく動作します。

<Grid.ColumnDefinitions>
    <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
    <ColumnDefinition/>
    <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
</Grid.ColumnDefinitions>

キャプション コントロール領域のサイズと位置は AppWindowTitleBar クラスによって伝達されるため、独自のタイトル バー UI のレイアウトでも、これを利用できます。 左右に予約されている領域の幅は LeftInset または RightInset プロパティで指定され、高さは Height プロパティで指定されます。

ドラッグ領域を計算して設定するときに、パディング列の幅を指定する方法を次に示します。

RightPaddingColumn.Width = 
    new GridLength(m_AppWindow.TitleBar.RightInset / scaleAdjustment);
LeftPaddingColumn.Width = 
    new GridLength(m_AppWindow.TitleBar.LeftInset / scaleAdjustment);

重要

表示倍率がこれらの値に与える影響については、「対話型コンテンツ」セクションの重要情報を参照してください。

カスタム タイトル バーでの縦長のタイトル バーのサポート

検索ボックスや人物の画像などの対話型コンテンツをタイトル バーに追加する場合は、タイトル バーを高さを増やして、このような要素の領域を確保することをお勧めします。 タイトル バーを高くすると、タッチ操作も簡単になります。 AppWindowTitleBar.PreferredHeightOption プロパティを使うと、タイトル バーの高さを既定の標準の高さよりも高くすることができます。 Tall タイトル バー モードを選ぶと、システムによってクライアント領域にオーバーレイとして描画されるキャプション ボタンは、より高くレンダリングされ、最小/最大/閉じるのグリフは中央に配置されます。 ドラッグ領域を指定していない場合、ウィンドウの幅と、設定した PreferredHeightOption 値によって決まる高さを拡張する領域がシステムによって描画されます。

この例は、PreferredHeightOption プロパティを設定する方法を示しています。

// A taller title bar is only supported when drawing a fully custom title bar.
if (ExtendsContentIntoTitleBar == true)
{
    m_AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
}

注意事項

PreferredHeightOption プロパティを設定する前に、AppWindowTitleBar.ExtendsContentIntoTitleBar プロパティを true にする必要があります。 ExtendsContentIntoTitleBarfalse である場合に PreferredHeightOption を設定しようとすると、例外がスローされます。

キャプション ボタンの色と透明度

アプリのコンテンツをタイトル バー領域に拡張する場合、キャプション ボタンの背景を透明にして、アプリの背景が透けて見えるようにすることができます。 通常、完全に透明にするには、背景を Colors.Transparent に設定します。 部分的に透明にする場合は、プロパティを設定した Color のアルファ チャネルを設定します。

これらのタイトル バーのプロパティは透明にすることができます。

他のすべての color プロパティは引き続きアルファ チャネルを無視します。 ExtendsContentIntoTitleBarfalse に設定されている場合、すべての AppWindowTitleBar color プロパティでアルファ チャネルは常に無視されます。

ボタンの背景色は、閉じるボタンの "ホバー" 時と "押下" 時の状態に適用されません。 それらの状態に対して、閉じるボタンには常にシステム定義の色が使われます。

ヒント

マイカは楽しい素材であり、フォーカスのあるウィンドウを区別するのに役立ちます。 Windows 11 で長時間使われるウィンドウの背景としてお勧めします。 ウィンドウのクライアント領域にマイカを適用した場合は、それをタイトル バー領域まで拡張し、マイカが透けて見えるようにキャプション ボタンを透明にすることができます。 詳細については、「マイカ素材」を参照してください。

ウィンドウが非アクティブなときにタイトル バーを暗くする

ウィンドウがアクティブか非アクティブかをわかりやすくしてください。 少なくとも、タイトル バーのテキスト、アイコン、ボタンの色を変更するようにします。

XAML アプリの場合、Window.Activated イベントを処理してウィンドウのアクティブ化状態を判断し、必要に応じてタイトル バー UI を更新します。

public MainWindow()
{
    ...
    Activated += MainWindow_Activated;
}

private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
    if (args.WindowActivationState == WindowActivationState.Deactivated)
    {
        TitleBarTextBlock.Foreground =
            (SolidColorBrush)App.Current.Resources["WindowCaptionForegroundDisabled"];
    }
    else
    {
        TitleBarTextBlock.Foreground =
            (SolidColorBrush)App.Current.Resources["WindowCaptionForeground"];
    }
}

他の UI フレームワークの場合は、イベントを処理してウィンドウのアクティブ化状態を判断し、必要に応じてタイトル バー UI を更新します。 ウィンドウの状態を確認する方法は、アプリに使う UI フレームワークによって異なります。

タイトル バーをリセットする

アプリの実行中にシステム タイトル バーをリセットするか切り替えるには、AppWindowTitleBar.ResetToDefault を呼び出します。

m_AppWindow.TitleBar.ResetToDefault();

XAML アプリの場合は、次の方法でタイトル バーをリセットすることもできます。

  • アプリの実行中に新しいタイトル バー要素に切り替えるには、SetTitleBar を呼び出します。
  • パラメーターとして null を指定して SetTitleBar を呼び出し、既定の AppWindowTitleBar ドラッグ領域にリセットします。
  • 既定のシステム タイトル バーに戻すには、パラメーターとして null を指定して SetTitleBar を呼び出し、ExtendsContentIntoTitleBarfalse に設定します。

タイトル バーの表示と非表示

"全画面表示" または "コンパクト オーバーレイ" モードのサポートをアプリに追加する場合、アプリがこれらのモード間を切り替えるときに、必要に応じてタイトル バーを変更します。 XAML ウィンドウには、全画面表示モードをサポートする API が用意されていません。この用途には AppWindow API を使用できます。

アプリが "全画面表示" モードで実行されている場合は、タイトル バーとキャプション コントロール ボタンが非表示になります。 AppWindow.Changed イベントを処理し、イベント引数の DidPresenterChange プロパティを確認して、新しいウィンドウのプレゼンテーションに応じてタイトル バーの表示、非表示、または変更を行う必要があるかどうかを判断できます。

この例は、前の例の Changed イベントを処理して、AppTitleBar 要素を表示および非表示にする方法を示しています。 ウィンドウが "コンパクト オーバーレイ" モードに設定されている場合、タイトル バーは既定のシステム タイトル バーにリセットされます (または、コンパクト オーバーレイ用に最適化されたカスタム タイトル バーを用意することもできます)。

public MainWindow()
{
    this.InitializeComponent();

    m_AppWindow = this.AppWindow;
    m_AppWindow.Changed += AppWindow_Changed;
}

private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
    if (args.DidPresenterChange)
    {
        switch (sender.Presenter.Kind)
        {
            case AppWindowPresenterKind.CompactOverlay:
                // Compact overlay - hide custom title bar
                // and use the default system title bar instead.
                AppTitleBar.Visibility = Visibility.Collapsed;
                sender.TitleBar.ResetToDefault();
                break;

            case AppWindowPresenterKind.FullScreen:
                // Full screen - hide the custom title bar
                // and the default system title bar.
                AppTitleBar.Visibility = Visibility.Collapsed;
                sender.TitleBar.ExtendsContentIntoTitleBar = true;
                break;

            case AppWindowPresenterKind.Overlapped:
                // Normal - hide the system title bar
                // and use the custom title bar instead.
                AppTitleBar.Visibility = Visibility.Visible;
                sender.TitleBar.ExtendsContentIntoTitleBar = true;
                break;

            default:
                // Use the default system title bar.
                sender.TitleBar.ResetToDefault();
                break;
        }
    }
}

Note

"全画面表示" と "コンパクト オーバーレイ" の各モードには、アプリでサポートされている場合にのみ切り替えることができます。 詳細については、「アプリ ウィンドウの管理」、FullScreenPresenterCompactOverlayPresenter のページを参照してください。

推奨と非推奨

  • ウィンドウがアクティブまたは非アクティブであるときには、それをわかりやすく示してください。 少なくとも、タイトル バーのテキスト、アイコン、ボタンの色を変更します。
  • ドラッグ領域は、アプリ キャンバスの上端に沿って定義します。 システム タイトル バーの配置に合わせると、ユーザーは見つけやすくなります。
  • ドラッグ領域は、アプリ キャンバス上に表示されるタイトル バー (ある場合) に合わせて定義します。

完全なカスタマイズの例

この例は、「完全なカスタマイズ」セクションで説明したすべてのコードを示しています。

<Window
    x:Class="WinUI3_CustomTitleBar.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Grid x:Name="AppTitleBar"
      Height="48">
            <Grid.ColumnDefinitions>
                <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
                <ColumnDefinition x:Name="IconColumn" Width="Auto"/>
                <ColumnDefinition x:Name="TitleColumn" Width="Auto"/>
                <ColumnDefinition x:Name="LeftDragColumn" Width="*"/>
                <ColumnDefinition x:Name="SearchColumn" Width="4*" MinWidth="220"/>
                <ColumnDefinition x:Name="RightDragColumn" Width="*" MinWidth="48"/>
                <ColumnDefinition x:Name="AccountColumn" Width="Auto"/>
                <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
            </Grid.ColumnDefinitions>
            <Image x:Name="TitleBarIcon" 
           Source="ms-appx:///Assets/StoreLogo.png"
           Grid.Column="1"
           Width="16" Height="16"
           Margin="8,0,4,0"/>
            <TextBlock x:Name="TitleBarTextBlock"
                       Text="App title" 
                       Style="{StaticResource CaptionTextBlockStyle}"
                       Grid.Column="2"
                       VerticalAlignment="Center">
            </TextBlock>
            <AutoSuggestBox x:Name="TitleBarSearchBox" 
                            Grid.Column="4" 
                            QueryIcon="Find"
                            PlaceholderText="Search"
                            VerticalAlignment="Center"
                            MaxWidth="600"/>
            <PersonPicture x:Name="PersonPic" 
                           Grid.Column="6" 
                           Height="32" Margin="0,0,16,0"/>
        </Grid>

        <NavigationView Grid.Row="1"
                        IsBackButtonVisible="Collapsed"
                        IsSettingsVisible="False">
            <StackPanel>
                <TextBlock Text="Content" 
                           Style="{ThemeResource TitleTextBlockStyle}"
                           Margin="32,0,0,0"/>
                <StackPanel Grid.Row="1" VerticalAlignment="Center">
                    <Button Margin="4" x:Name="CompactoverlaytBtn"
                            Content="Enter CompactOverlay"
                            Click="SwitchPresenter"/>
                    <Button Margin="4" x:Name="FullscreenBtn" 
                            Content="Enter FullScreen"
                            Click="SwitchPresenter"/>
                    <Button Margin="4" x:Name="OverlappedBtn"
                            Content="Revert to default (Overlapped)"
                            Click="SwitchPresenter"/>
                </StackPanel>
            </StackPanel>
        </NavigationView>
    </Grid>
</Window>
using Microsoft.UI.Input;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System;
using Windows.ApplicationModel;
using Rect = Windows.Foundation.Rect;

public sealed partial class MainWindow : Window
{
    private AppWindow m_AppWindow;

    public MainWindow()
    {
        this.InitializeComponent();

        // Assumes "this" is a XAML Window. In projects that don't use 
        // WinUI 3 1.3 or later, use interop APIs to get the AppWindow.
        m_AppWindow = this.AppWindow;
        m_AppWindow.Changed += AppWindow_Changed;
        Activated += MainWindow_Activated;
        AppTitleBar.SizeChanged += AppTitleBar_SizeChanged;
        AppTitleBar.Loaded += AppTitleBar_Loaded;

        ExtendsContentIntoTitleBar = true;
        if (ExtendsContentIntoTitleBar == true)
        {
            m_AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
        }
        TitleBarTextBlock.Text = AppInfo.Current.DisplayInfo.DisplayName;
    }

    private void AppTitleBar_Loaded(object sender, RoutedEventArgs e)
        {
            if (ExtendsContentIntoTitleBar == true)
            {
                // Set the initial interactive regions.
                SetRegionsForCustomTitleBar();
            }
        }

    private void AppTitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (ExtendsContentIntoTitleBar == true)
            {
                // Update interactive regions if the size of the window changes.
                SetRegionsForCustomTitleBar();
            }
        }

    private void SetRegionsForCustomTitleBar()
    {
        // Specify the interactive regions of the title bar.

        double scaleAdjustment = AppTitleBar.XamlRoot.RasterizationScale;

        RightPaddingColumn.Width = new GridLength(m_AppWindow.TitleBar.RightInset / scaleAdjustment);
        LeftPaddingColumn.Width = new GridLength(m_AppWindow.TitleBar.LeftInset / scaleAdjustment);

        // Get the rectangle around the AutoSuggestBox control.
        GeneralTransform transform = TitleBarSearchBox.TransformToVisual(null);
        Rect bounds = transform.TransformBounds(new Rect(0, 0,
                                                         TitleBarSearchBox.ActualWidth,
                                                         TitleBarSearchBox.ActualHeight));
        Windows.Graphics.RectInt32 SearchBoxRect = GetRect(bounds, scaleAdjustment);

        // Get the rectangle around the PersonPicture control.
        transform = PersonPic.TransformToVisual(null);
        bounds = transform.TransformBounds(new Rect(0, 0,
                                                    PersonPic.ActualWidth,
                                                    PersonPic.ActualHeight));
        Windows.Graphics.RectInt32 PersonPicRect = GetRect(bounds, scaleAdjustment);

        var rectArray = new Windows.Graphics.RectInt32[] { SearchBoxRect, PersonPicRect };

        InputNonClientPointerSource nonClientInputSrc =
            InputNonClientPointerSource.GetForWindowId(this.AppWindow.Id);
        nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, rectArray);
    }

    private Windows.Graphics.RectInt32 GetRect(Rect bounds, double scale)
    {
        return new Windows.Graphics.RectInt32(
            _X: (int)Math.Round(bounds.X * scale),
            _Y: (int)Math.Round(bounds.Y * scale),
            _Width: (int)Math.Round(bounds.Width * scale),
            _Height: (int)Math.Round(bounds.Height * scale)
        );
    }

    private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
    {
        if (args.WindowActivationState == WindowActivationState.Deactivated)
        {
            TitleBarTextBlock.Foreground =
                (SolidColorBrush)App.Current.Resources["WindowCaptionForegroundDisabled"];
        }
        else
        {
            TitleBarTextBlock.Foreground =
                (SolidColorBrush)App.Current.Resources["WindowCaptionForeground"];
        }
    }

    private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
    {
        if (args.DidPresenterChange)
        {
            switch (sender.Presenter.Kind)
            {
                case AppWindowPresenterKind.CompactOverlay:
                    // Compact overlay - hide custom title bar
                    // and use the default system title bar instead.
                    AppTitleBar.Visibility = Visibility.Collapsed;
                    sender.TitleBar.ResetToDefault();
                    break;

                case AppWindowPresenterKind.FullScreen:
                    // Full screen - hide the custom title bar
                    // and the default system title bar.
                    AppTitleBar.Visibility = Visibility.Collapsed;
                    sender.TitleBar.ExtendsContentIntoTitleBar = true;
                    break;

                case AppWindowPresenterKind.Overlapped:
                    // Normal - hide the system title bar
                    // and use the custom title bar instead.
                    AppTitleBar.Visibility = Visibility.Visible;
                    sender.TitleBar.ExtendsContentIntoTitleBar = true;
                    break;

                default:
                    // Use the default system title bar.
                    sender.TitleBar.ResetToDefault();
                    break;
            }
        }
    }

    private void SwitchPresenter(object sender, RoutedEventArgs e)
    {
        if (AppWindow != null)
        {
            AppWindowPresenterKind newPresenterKind;
            switch ((sender as Button).Name)
            {
                case "CompactoverlaytBtn":
                    newPresenterKind = AppWindowPresenterKind.CompactOverlay;
                    break;

                case "FullscreenBtn":
                    newPresenterKind = AppWindowPresenterKind.FullScreen;
                    break;

                case "OverlappedBtn":
                    newPresenterKind = AppWindowPresenterKind.Overlapped;
                    break;

                default:
                    newPresenterKind = AppWindowPresenterKind.Default;
                    break;
            }

            // If the same presenter button was pressed as the
            // mode we're in, toggle the window back to Default.
            if (newPresenterKind == AppWindow.Presenter.Kind)
            {
                AppWindow.SetPresenter(AppWindowPresenterKind.Default);
            }
            else
            {
                // Else request a presenter of the selected kind
                // to be created and applied to the window.
                AppWindow.SetPresenter(newPresenterKind);
            }
        }
    }
}