Delphi11で追加されたVCLのWindows11スタイルを利用する

本投稿は[Delphi Advent Calendar 2021 #04](https://qiita.com/advent-calendar/2021/delphi) の記事です。

以下の記事元にDelphi11で追加されたWindwos11スタイルに変更しました。このプログラムはWindows10で確認することができます。

https://qiita.com/p_kato/items/88411578b7091ab1fcd0

#ダークモードの切り替えについて
Windows10のMay 2019 Updateで追加された機能で，デスクトップを右クリックして表示されるポップアップメニューから，**個人用設定**を選択すると設定ダイアログが表示されます。左側の**色タブ**を選択し，**色を選択する**から**ダーク**を選ぶとダークモードに変わり，**ライト**を選ぶと通常のモード(ライトモード)になります。
アクセントカラーもこのダイアログで変更することができます。

#Delphi11でVCLのスタイルを追加する
まずはDelphi11で追加されたWindows11のダークスタイルとライトスタイルをプロジェクトに適応してみましょう。

####新規プロジェクトを作成する
Delphi11を起動し，Windwos VCLアプリケーションを新規作成します。

####プロジェクトにスタイルを追加する
**プロジェクト**の**オプション**を開きます。
![20211204_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33902/3f469218-9a45-1d15-0c9d-47754e5ec82c.png)
次に，**表示**を選択し，**カスタムスタイル**で，*Windows11 Modern Dark*と*Windows11 Modern Light*をチェックし，デフォルトスタイルを*Windows11 Modern Light*にします。
![20211204_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33902/fea6cff1-84ba-ae88-233c-506ed623ea17.png)
選択が終わったら，**保存**ボタンをクリックします。

####フォームにスタイルを確認したいコンポーネントをのせる
フォームの編集をします。

[RAD Studio 10.3.1の新しいVCLとFireMonkey向けスタイル Embarcadero Blog](https://blogs.embarcadero.com/ja/new-vcl-and-firemonkey-styles-in-rad-studio-10-3-1-ja/)を参考にコンポーネントを配置してみました。

アクセントカラーを表示するためのShape1を追加しています。

![20211204_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33902/6ae5d83a-3fee-2a86-c62c-5d7c1cd15faf.png)
ToolbarのToolButtonのキャプションを出すにはTToolBar.ShowCaptionsをTrueにしなくてはならなかったり，TStringGridのカラムの文字をのせるのに右クリックでデザイナを選べないので仕方なくプログラムで文字を入れたり(なので編集画面の画像で文字が出てません，，)，カテゴリボタンのスタイル設定が分からず，表示がいまいちだったりしています。もしかすると違うコンポーネントかも，，詳しい方ぜひ良い方法を教えてください！

####実行してみる
設定で，デフォルトスタイルを指定しましたので，実行するとWindows11のスタイルが適用されます。
![20211204_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33902/ad7fbfaf-fce5-9e17-6429-f6830cf942a2.png)
全てのコントロールに，Windows11風のスタイルが適用されています。ウインドウの角が丸くなっているのが新鮮ですね。よく考えたら，WindowsXPのデザインもそうだったような気がします。角の丸みはWindows11のものより若干小さいように見えますが，Windows10でも同じデザインが適用されるのは良いことのように思います。

####ダークモードの変更に応じてVCLのテーマを入れ替える
アクセントからの変更にも対応したライブラリを作っています。[前回紹介したもの](https://qiita.com/p_kato/items/88411578b7091ab1fcd0)と同じです。

```uTheme.pas
unit uTheme;

interface

uses
  Winapi.Messages;

type
  TisATF=(fdAuto,fdTrue,fdFalse);           // 自動と強制設定を管理する

type
  TColorF=record
  case Cardinal of
    0: (C: Cardinal);
    1: (R, G, B, A: System.Byte);
  end;

var
  isWindows10:boolean; // Windows10か？
  AccentColor:TColorF; // アクセントカラー

function isLightTheme:boolean;              // LightThemeかDarkThemeか
procedure isLightThemeSet(AisATF:TisATF);
procedure isLightThemeUpdate;

function ThemetFormWndProc(var Message: TMessage):boolean; // テーマ更新の監視

implementation

uses
  System.SysUtils,
  System.Win.Registry,
  Winapi.Windows;

procedure isWindows10Set; // Windows10か？
begin
  isWindows10:=CheckWin32Version(10, 0);
end;

function SysAccentColor:TColorF; // アクセントカラー
var
 Registry: TRegistry;
begin
   Result.C:=$00000000;
   Registry := TRegistry.Create(KEY_READ);
   try
     Registry.RootKey := HKEY_CURRENT_USER;
     Registry.OpenKey('Software\Microsoft\Windows\DWM', False);
     Result.C :=Cardinal((Registry.ReadInteger('AccentColor')) or $FF000000);
   finally
     Registry.Free;
   end;
end;

procedure SysAccentColorUpdate(wParam:NativeUInt);
var
  d:TColorF;
begin
  d.C:=wParam or $FF000000;
  AccentColor.R:=d.B;
  AccentColor.G:=d.G;
  AccentColor.B:=d.R;
  AccentColor.A:=$FF;
end;

function isSysLightTheme:boolean; // テーマは Light / Dark か？
var
 Registry: TRegistry;
begin
  Result:=True;
  Registry := TRegistry.Create(KEY_READ);
  try
    Registry.RootKey:= HKEY_CURRENT_USER;
    if Registry.OpenKey('Software\Microsoft\Windows\CurrentVersion\Themes\Personalize', False) then begin
      Result := (Registry.ReadInteger('AppsUseLightTheme')=1);
    end;
  finally
    Registry.Free;
  end;
end;

var
  FisLightTheme:integer=1;

function isLightTheme:boolean;
begin
  Result:=((FisLightTheme and 1)=1)or(not isWindows10); // Windows7は常にLightTheme
end;

procedure isLightThemeAutoSet;
begin
  if isSysLightTheme then begin
    FisLightTheme:=1;
  end else begin
    FisLightTheme:=0;
  end;
end;

procedure isLightThemeSet(AisATF:TisATF); // テーマをセットする
begin
  case AisATF of
    fdAuto: isLightThemeAutoSet;
    fdTrue: FisLightTheme:=3;
   fdFalse: FisLightTheme:=2;
  end;
end;

procedure isLightThemeUpdate; // 自動更新なら更新する
begin
  if (FisLightTheme and 2)=0 then isLightThemeAutoSet;
end;

function AccentColorWndProc(var Message: TMessage):boolean; // アクセントカラーの監視
// 親フォームのWndProcで監視する
var
  ba:boolean;
begin
  ba:=Message.Msg = WM_DWMCOLORIZATIONCOLORCHANGED;
  if ba then begin
    SysAccentColorUpdate(Message.WParam);  // アクセントカラーの変更
  end;
  Result:=ba;
end;

function ThemetFormWndProc(var Message: TMessage):boolean; // テーマ更新の監視
// 親フォームのWndProcで監視する
var
  ba,bt:boolean;
begin
  ba:=AccentColorWndProc(Message); // アクセントカラーの監視
  bt:=Message.Msg = WM_SETTINGCHANGE;
  if bt then begin
    isLightThemeUpdate; // テーマ カラーの変更
  end;
  Result:=ba or bt;
end;

initialization
  isWindows10Set; // isWindwos10をセット
  AccentColor:=SysAccentColor; // アクセントカラー
  isLightThemeSet(fdAuto);     // デフォルトは自動
end.
```

次に，このユニットを使うように書き換えます。
フォームにShape1を貼り付けて，アクセントカラーを表示しています。

```Unit1.pas
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, Vcl.ComCtrls, Vcl.ToolWin,
  Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.CheckLst, Vcl.Grids, Vcl.CategoryButtons;

type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Edit1: TMenuItem;
    Help1: TMenuItem;
    demo1: TMenuItem;
    demo2: TMenuItem;
    demo3: TMenuItem;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    PopupMenu1: TPopupMenu;
    menu11: TMenuItem;
    menu21: TMenuItem;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    TabSheet3: TTabSheet;
    StatusBar1: TStatusBar;
    Button1: TButton;
    Label1: TLabel;
    Edit2: TEdit;
    ComboBox1: TComboBox;
    CheckBox1: TCheckBox;
    TrackBar1: TTrackBar;
    ProgressBar1: TProgressBar;
    StringGrid1: TStringGrid;
    ListBox1: TListBox;
    CheckListBox1: TCheckListBox;
    RadioGroup1: TRadioGroup;
    CategoryButtons1: TCategoryButtons;
    Shape1: TShape;
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    procedure ThemeRepaint;
    procedure WndProc(var Message: TMessage);override;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  uTheme,
  Vcl.Themes,
  Vcl.Styles;

procedure TForm1.ThemeRepaint; // フォームのスタイルを更新
begin
  if isLightTheme then begin
    TStyleManager.TrySetStyle('Windows11 Modern Light');
  end else begin
    TStyleManager.TrySetStyle('Windows11 Modern Dark');
  end;
  Shape1.Brush.Color:=TColor(AccentColor.C and $00FFFFFF);
  Repaint;
end;

procedure TForm1.WndProc(var Message: TMessage); // モードを監視
begin
  if ThemetFormWndProc(Message) then begin
    ThemeRepaint;
  end;
  inherited WndProc(Message);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ThemeRepaint;
end;

end.
```

スタイル名が**Windows11 Modern Dark**と**Windows11 Modern Light**に変わっています。
WndProcをオーバーライドしてWM_DWMCOLORIZATIONCOLORCHANGEDとWM_SETTINGCHANGEを監視して，変更があればレジストリからダークテーマの情報をWPARAMから新しいアクセントカラーを取得して，フォームのカスタムスタイルを変更しています。

ライトモードでは以下のように表示されます。
![20211204_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33902/674e1d0b-8efe-8855-055f-2b3eb81b7068.png)
ダークモードでは以下のように表示されます。
![20211204_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33902/3028f2ab-ec9d-1e71-1c63-4fbb26ddeabb.png)
プログレスバーの色が緑なのがちょっと残念ですね。青だったらよかったんですが，，

また，以下のようにアクセントカラーを変えても，スタイルには影響がありませんでした。
![20211204_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33902/22946f67-968a-bb0c-97f8-b339877355f5.png)
この画像では，アクセントカラーが赤になっていますが，スタイルは青色を基調にしています。
アクセントカラーに対応した，スタイルって作るのは難しそうですね，，

ダークモードに対応したこの機能は，またすぐに動かなくなるかもしれませんが，皆様のお役に立てたらうれしいです。

#参考資料

- [ToolButtonのCaptionに文字を入力しても表示されません](https://www.petitmonte.com/bbs/answers?question_id=4274)
- [TStringGrid の使い方](https://ht-deko.com/techf011.html)
- [Windows 10 でUIをダークテーマ (ダークモード) に変更する (Windows 10 Tips)](https://www.ipentec.com/document/windows-windows-10-change-dark-theme)
- iPentec
[VCL スタイル (Delphi XE2 以降)](https://ht-deko.com/tech074.html) - DEKOのアヤシいお部屋。
- [クラシック デスクトップ アプリの Windows テーマ追従](http://grabacr.net/archives/6671) - grabacr.nét PaaS (Patchouli as a Shachiku)
- [RAD Studio 10.3.1の新しいVCLとFireMonkey向けスタイル Embarcadero Blog](https://blogs.embarcadero.com/ja/new-vcl-and-firemonkey-styles-in-rad-studio-10-3-1-ja/)
貴重な情報を提供してくださった皆様，ありがとうございます。感謝します。