Windows FormアプリケーションでWindows 11のタイトルバーのカスタマイズ機能を利用する - C#

Windows FormアプリケーションでWindows 11のタイトルバーのカスタマイズ機能を利用するコードを紹介します。

概要

こちらの記事ではWinUI 3アプリケーションでのタイトルバーのカスタマイズの手順を紹介しました。
この記事では同様のカスタマイズをWindows Form アプリケーションに実装する方法を紹介します。

実装方針

Windows Formアプリケーションの場合もWinUI 3と同様に、Microsoft.UI.Windowing.AppWindow オブジェクトを取得し、TitleBarプロパティを変更します。

プログラム

Windows Formアプリケーションを作成します。Windows App SDKは.NET Framework には対応していないため、.NET 6のWindows Formアプリケーションを作成します。

事前準備:パッケージのインストール

Microsoft.WindowsAppSDK 1.1 以降のパッケージをインストールします。
NuGetパッケージマネージャーコンソールを利用する場合は以下のコマンドを実行します。
Install-Package Microsoft.WindowsAppSDK -Version (インストールするバージョン)

事前準備:プロジェクトファイルの設定

Windows App SDKは現時点では、ARMには対応していないため、Platforms にx86,x64のみを記述し、RuntimeIdentifiersを追記します。
自己完結型のプログラムにするため、以下のXMLを追加します。
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<WindowsPackageType>None</WindowsPackageType>
プロジェクトファイル
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWindowsForms>true</UseWindowsForms>
    <ImplicitUsings>enable</ImplicitUsings>
    <Platforms>x86;x64</Platforms>
    <RuntimeIdentifiers>win10-x86;win10-x64</RuntimeIdentifiers>
    <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
    <WindowsPackageType>None</WindowsPackageType>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.4" />
  </ItemGroup>

</Project>

UI

下図のフォームを作成します。
FlowLayoutPanelをウィンドウ上部に配置します。FlowLayoutPanelのVisibleプロパティはFalseに設定しておきます。 このFlowLayoutPanelがタイトルバーになりますので、LabelコントロールをFlowLayoutPanel内に配置して、タイトルバーのキャプションを表現します。
また、ウィンドウ領域にボタンを2つ配置します。

コード

下記コードを記述します。
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Windows.UI.ViewManagement;
using Windows.UI.WindowManagement;

namespace AppWindowDemo
{
  public partial class FormMain : Form
  {
    private Microsoft.UI.Windowing.AppWindow _mainAppWindow;

    public FormMain()
    {
      InitializeComponent();

      IntPtr hwnd = this.Handle;
      WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
      _mainAppWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);

      _mainAppWindow.Changed += AppWindowChangedHandler;
      _mainAppWindow.Title = "タイトルです";
    }

    public void Form1_Load(object sender, EventArgs e)
    {
    }

    private void button1_Click(object sender, EventArgs e)
    {
      if (Microsoft.UI.Windowing.AppWindowTitleBar.IsCustomizationSupported() == true) {

        _mainAppWindow.TitleBar.ForegroundColor = Colors.White;
        _mainAppWindow.TitleBar.BackgroundColor = Colors.SkyBlue;

        _mainAppWindow.TitleBar.InactiveForegroundColor = Colors.White;
        _mainAppWindow.TitleBar.InactiveBackgroundColor = Colors.DeepSkyBlue;

        _mainAppWindow.TitleBar.ButtonBackgroundColor = Colors.Orange;
        _mainAppWindow.TitleBar.ButtonForegroundColor = Colors.White;
        _mainAppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Olive;
        _mainAppWindow.TitleBar.ButtonInactiveForegroundColor = Colors.White;
        _mainAppWindow.TitleBar.ButtonHoverBackgroundColor = Colors.Green;
        _mainAppWindow.TitleBar.ButtonHoverForegroundColor = Colors.White;
        _mainAppWindow.TitleBar.ButtonPressedBackgroundColor = Colors.Gold;
        _mainAppWindow.TitleBar.ButtonPressedForegroundColor = Colors.White;
      }
    }

    private void button2_Click(object sender, EventArgs e)
    {
      if (Microsoft.UI.Windowing.AppWindowTitleBar.IsCustomizationSupported() == true) {
        UISettings us = new UISettings();
        Windows.UI.Color c1 = us.GetColorValue(UIColorType.Background);
        Color cc1 = Color.FromArgb(c1.A, c1.R, c1.G, c1.B);
        flowLayoutPanel1.BackColor = cc1;

        Windows.UI.Color c2 = us.GetColorValue(UIColorType.Foreground);
        Color cc2 = Color.FromArgb(c2.A, c2.R, c2.G, c2.B);
        label1.ForeColor = cc2;

        flowLayoutPanel1.Visible = true;

        _mainAppWindow.TitleBar.ExtendsContentIntoTitleBar = true;
        _mainAppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;

        SetDragRegionForCustomTitleBar(_mainAppWindow);
      }
    }

    private void AppWindowChangedHandler(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
    {
      if (args.DidPresenterChange) {
      }
      if (args.DidSizeChange && sender.TitleBar.ExtendsContentIntoTitleBar) {
        SetDragRegionForCustomTitleBar(sender);
      }
      if (args.DidSizeChange) {
        flowLayoutPanel1.Width = _mainAppWindow.Size.Width;
      }
    }

    private void SetDragRegionForCustomTitleBar(Microsoft.UI.Windowing.AppWindow appWindow)
    {
      int titleBarHeight = appWindow.TitleBar.Height;
      flowLayoutPanel1.Height = titleBarHeight;

      int CaptionButtonOcclusionWidth = appWindow.TitleBar.RightInset;

      Windows.Graphics.RectInt32[] dragRects = new Windows.Graphics.RectInt32[] { };
      Windows.Graphics.RectInt32 dragRect;

      dragRect.X = 0;
      dragRect.Y = 0;
      dragRect.Height = titleBarHeight;
      dragRect.Width = appWindow.Size.Width;

      appWindow.TitleBar.SetDragRectangles(dragRects.Append(dragRect).ToArray());
    }
  }
}

解説

button1

タイトルバーのカスタマイズ機能は、Windows 11以降でしか対応していないため、初めに、IsCustomizationSupported()メソッドを呼び出し、 カスタマイズ機能に対応しているか確認します。
カスタマイズできる場合は、AppWindow オブジェクトの TitleBarオブジェクトのプロパティの値を変更し、タイトルバーをカスタマイズします。
  if (Microsoft.UI.Windowing.AppWindowTitleBar.IsCustomizationSupported() == true) {
    _mainAppWindow.TitleBar.ForegroundColor = Colors.White;
    _mainAppWindow.TitleBar.BackgroundColor = Colors.SkyBlue;

    _mainAppWindow.TitleBar.InactiveForegroundColor = Colors.White;
    _mainAppWindow.TitleBar.InactiveBackgroundColor = Colors.DeepSkyBlue;

    _mainAppWindow.TitleBar.ButtonBackgroundColor = Colors.Orange;
    _mainAppWindow.TitleBar.ButtonForegroundColor = Colors.White;
    _mainAppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Olive;
    _mainAppWindow.TitleBar.ButtonInactiveForegroundColor = Colors.White;
    _mainAppWindow.TitleBar.ButtonHoverBackgroundColor = Colors.Green;
    _mainAppWindow.TitleBar.ButtonHoverForegroundColor = Colors.White;
    _mainAppWindow.TitleBar.ButtonPressedBackgroundColor = Colors.Gold;
    _mainAppWindow.TitleBar.ButtonPressedForegroundColor = Colors.White;
  }

button2

button2 は完全に独自のタイトルバーにカスタマイズする方法です。
AppWindow.TitleBar.ExtendsContentIntoTitleBar のプロパティを true に設定することで、既存のツールバーの描画がなくなり、 ウィンドウ領域でツールバーを描画する動作になります。
今回の例では、flowLayoutPanelを表示し、タイトルバーとして画面に表示しています。

flowLayoutPanelのBackColor は現在のカラーテーマのBackgroundカラーを設定します。
label1の文字色(ForeColor)には現在のカラーテーマのForegroundカラーを設定します。
  if (Microsoft.UI.Windowing.AppWindowTitleBar.IsCustomizationSupported() == true) {
    UISettings us = new UISettings();
    Windows.UI.Color c1 = us.GetColorValue(UIColorType.Background);
    Color cc1 = Color.FromArgb(c1.A, c1.R, c1.G, c1.B);
    flowLayoutPanel1.BackColor = cc1;

    Windows.UI.Color c2 = us.GetColorValue(UIColorType.Foreground);
    Color cc2 = Color.FromArgb(c2.A, c2.R, c2.G, c2.B);
    label1.ForeColor = cc2;

    flowLayoutPanel1.Visible = true;

    _mainAppWindow.TitleBar.ExtendsContentIntoTitleBar = true;
    _mainAppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;

    SetDragRegionForCustomTitleBar(_mainAppWindow);
  }

ウィンドウサイズが変わると、タイトルバーとして表示している、flowLayoutPanel1の大きさも追従して変更する必要があります。
タイトルバーの変更は、AppWindowChangedHandler で検出します。
ウィンドウサイズが変更された場合は、flowLayoutPanel1の横幅を変更します。また、ウィンドウを移動させることのできるドラッグ範囲も併せて拡張します。
  private void AppWindowChangedHandler(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
    {
      if (args.DidPresenterChange) {
      }
      if (args.DidSizeChange && sender.TitleBar.ExtendsContentIntoTitleBar) {
        SetDragRegionForCustomTitleBar(sender);
      }
      if (args.DidSizeChange) {
        flowLayoutPanel1.Width = _mainAppWindow.Size.Width;
      }
    }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[button1]をクリックします。タイトルバーの色やウィンドウ右上のキャプションコントロールボタンの色が変化します。


キャプションボタンにマウスポインタが重なった際の色やクリックした際のボタンカラーも設定した色に変わります。



ウィンドウが非アクティブになった場合は下図の表示になります。こちらも設定した色が反映されています。


続いてアプリケーションを再起動し、[button2]ボタンをクリックします。
システムのカラーテーマが"ライト"の場合は下図の表示になります。タイトルバーの高さが通常より広くなっていることがわかります。 また、FlowLayoutPanelに配置したキャプションのLabelコントロールも表示できています。


システムのカラーテーマが"ダーク"の場合は、下図の表示になります。テーマのカラーがタイトルバーに反映できています。


Microsoft.UI.Windowing.AppWindow オブジェクトを利用してWindows 11のタイトルバーカスタマイズ機能をWindows Formアプリで使用しました。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2022-09-05
作成日: 2022-08-12
iPentec all rights reserverd.