はじめに
WinUI 3 Galleryのように、タイトルバーを独自にカスタマイズしたいと思ったことはありませんか?
WinUI 3 (Windows App SDK) ではタイトルバーの拡張が可能ですが、単にカスタムタイトルバーを設定するだけでは不十分です。クリック判定の制御や、OSのシステムボタン(閉じる・最大化・最小化)との色合わせなど、実戦で必要なTipsを共有します。
1. 基礎:コンテンツをタイトルバーまで広げる
まず、標準のタイトルバーを非表示にし、カスタムTitleBarをその領域まで食い込ませる設定とシステムボタンの高さをそろえる設定が必要です。
private AppWindow _appWindow;
public MainWindow()
{
InitializeComponent();
IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
Microsoft.UI.WindowId windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
_appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
ExtendsContentIntoTitleBar = true;
if (Microsoft.UI.Windowing.AppWindowTitleBar.IsCustomizationSupported())
{
var appTitleBar = _appWindow.TitleBar;
// 【ここが重要】タイトルバーの高さを「Tall (高い)」に設定
// これにより、システムボタンの高さが自動的に拡張されます
appTitleBar.PreferredHeightOption = Microsoft.UI.Windowing.TitleBarHeightOption.Tall;
appTitleBar.ButtonBackgroundColor = Microsoft.UI.Colors.Transparent;
appTitleBar.ButtonInactiveBackgroundColor = Microsoft.UI.Colors.Transparent;
// XAML上の TitleBar コントロールをシステムに認識させる
SetTitleBar(titleBar);
// 初回の色設定
UpdateTitleBarButtonColors();
}
// UIが完全に表示されてから、ボタンの色を変更する
RootGrid.ActualThemeChanged += (s, e) => UpdateTitleBarButtonColors();
// 画面遷移が発生した後に実行される共通処理を登録
ContentFrame.Navigated += ContentFrame_Navigated;
}
private void ContentFrame_Navigated(object sender, NavigationEventArgs e)
{
// 1. 戻るボタンの表示・非表示を切り替え
// ホーム画面(履歴の最初)では戻るボタンを隠す
titleBar.IsBackButtonVisible = ContentFrame.CanGoBack;
// 2. NavigationView の選択項目を現在のページに合わせる
UpdateNavigationViewSelection(e.SourcePageType);
}
private void UpdateNavigationViewSelection(Type currentPageType)
{
string? tag = currentPageType.Name switch
{
"HomePage" => "HomePage",
"PackagePage" => "PackagePage",
"EditorPage" => "EditorPage",
"SimulatorPage" => "SimulatorPage",
"LinePage" => "LinePage",
"FriendsPage" => "FriendsPage",
"SettingsPage" => "SettingsPage",
_ => null
};
if (tag == "SettingsPage")
{
// SettingsPage は特殊扱い(SettingsItem)なので、直接選択状態を設定
NavView.SelectedItem = NavView.SettingsItem;
}
else
{
var item = NavView.MenuItems
.OfType<NavigationViewItem>()
.FirstOrDefault(i => i.Tag?.ToString() == tag);
if (item != null)
{
NavView.SelectedItem = item;
}
}
}
2. XAMLでのレイアウト:操作領域の確保
タイトルバーの中にボタンを配置する場合、「ウィンドウをドラッグできる場所」 と 「ボタンをクリックできる場所」 を切り分ける必要があります。
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="LichMenuLab.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LichMenuLab">
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid x:Name="RootGrid" Loaded="RootGrid_Loaded">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar
x:Name="titleBar"
Title="LichMenuLab"
Grid.Row="0"
Height="48"
x:FieldModifier="public"
BackRequested="titleBar_BackRequested"
IsBackButtonVisible="False"
IsPaneToggleButtonVisible="True"
PaneToggleRequested="titleBar_PaneToggleRequested">
<TitleBar.IconSource>
<ImageIconSource ImageSource="ms-appx:///Assets/LichMenuLab.ico" />
</TitleBar.IconSource>
<TitleBar.RightHeader>
<StackPanel
VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="16">
<Button
Height="30"
Padding="15,0"
Background="Orange"
CornerRadius="15"
FontSize="16"
FontWeight="Bold"
Foreground="White">
<Button.Resources>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="DarkOrange" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="White" />
</Button.Resources>
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="18" Glyph="" />
<TextBlock Text="Pro版へアップグレード" />
</StackPanel>
</Button>
<Button
Width="30"
Height="30"
Padding="0"
Background="DarkGray"
CornerRadius="15"
Foreground="White">
<Button.Resources>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="Gray" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="White" />
</Button.Resources>
<FontIcon FontSize="18" Glyph="" />
</Button>
</StackPanel>
</TitleBar.RightHeader>
</TitleBar>
<NavigationView
x:Name="NavView"
Grid.Row="1"
x:FieldModifier="public"
IsBackButtonVisible="Collapsed"
IsPaneToggleButtonVisible="False"
IsSettingsVisible="True"
ItemInvoked="NavView_ItemInvoked"
PaneDisplayMode="Left">
<NavigationView.MenuItems>
<NavigationViewItem
Content="ホーム"
Icon="Home"
Tag="HomePage" />
<NavigationViewItem
Content="パッケージ"
Tag="PackagePage">
<NavigationViewItem.Icon>
<FontIcon Glyph="" />
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItem
Content="エディタ"
Icon="Edit"
Tag="EditorPage" />
<NavigationViewItem
Content="シミュレーター"
Icon="CellPhone"
Tag="SimulatorPage" />
<NavigationViewItem
Content="Line管理"
Icon="Share"
Tag="LinePage" />
<NavigationViewItem
Content="友だち(プレビュー機能)"
Icon="People"
Tag="FriendsPage" />
</NavigationView.MenuItems>
<!-- Frame をここに配置 -->
<Frame x:Name="ContentFrame" />
</NavigationView>
</Window>
Tip: 専用の TitleBar コントロールが提供されており、これを使ってレイアウトをします。
3. システムボタンの色調整
タイトルバーを独自の色にしたり、ダーク/ライトテーマを切り替えたりすると、システムボタンが見えなくなります。これはコードビハインドから AppWindowTitleBar を操作して解決します。
private void UpdateTitleBarButtonColors()
{
var appWindow = GetAppWindowForCurrentWindow();
if (AppWindowTitleBar.IsCustomizationSupported())
{
var titleBar = appWindow.TitleBar;
var theme = RootGrid.ActualTheme;
if (theme == ElementTheme.Dark)
{
titleBar.ButtonForegroundColor = Colors.White;
titleBar.ButtonHoverBackgroundColor = Color.FromArgb(20, 255, 255, 255);
}
else
{
titleBar.ButtonForegroundColor = Colors.Black;
titleBar.ButtonHoverBackgroundColor = Color.FromArgb(20, 0, 0, 0);
}
}
}
4. 動的な制御:ナビゲーションとの連動
アプリの構成(例えばメニューを左側に置くか、上部に置くか)によって、タイトルバーの「ハンバーガーボタン」の表示・非表示を切り替えたい場合があります。
LichMenuLabでは、設定変更時に以下のように IsPaneToggleButtonVisible を制御しています。
public void ApplyNavStyle(int styleIndex)
{
// 上部ナビゲーション時はタイトルバーのトグルを隠す
titleBar.IsPaneToggleButtonVisible = (styleIndex == 0);
}
5. 戻るボタンの処理
private void titleBar_BackRequested(TitleBar sender, object args)
{
if (ContentFrame.CanGoBack)
{
ContentFrame.GoBack();
}
}
6. ハマりどころ:難読化ツールの影響
ここが一番のポイントです。難読化ツール(Obfuscarなど)を導入すると、タイトルバーに関連するプロパティ名がリネームされ、XAMLとの紐付けが壊れてクラッシュ(0xC000027B)することがあります。
解決策:
obfuscar.xml で、MainWindow や LichMenuLab_XamlTypeInfo 名前空間を明示的に難読化(リネーム)の対象外に設定しましょう。
最後に
カスタムタイトルバーは、単なる「見た目」の問題ではなく、限られたデスクトップの画面領域を有効活用するための「UI設計」そのものです。
今回紹介したTipsは、私が開発・公開している 「LichMenuLab」 というアプリで実際に運用しているものです。
LichMenuLab は、LINE公式アカウントのリッチメニューを、コード不要・シミュレーター付きで設計できるツールです。今回解説した「テーマ対応タイトルバー」や「AIアシスタントの統合」がどのように動作するか、ぜひストアで実機を確認してみてください!
LichMenuLab:タブ付きリッチメニュー簡単作成
https://apps.microsoft.com/detail/9NBD0H4LQNX1