single.php

C# WPFアプリのタイトルバーをカスタマイズする方法

C# WinFormsアプリで同じようなことをしようとして、行き詰ったのでWPFアプリでタイトルバーの色などをカスタマイズする方法を備忘録的に投稿します。

WinFormsアプリで行き詰っているのに興味があれば別記事をご覧ください。

WinFormsアプリの無理ゲーに近くなっている

フォームの[FormBorderStyle]プロパティを変更してToolStripコントロールを使ってニセのタイトルバーのようにして、角を丸めると下の画像のようにWindows11のアプリっぽくなります。

しかし、致命的な2点が問題点として残ります。

1つめはマルチモニター環境で拡大率の異なる画面を跨ってウィンドウをドラッグで移動させる場合に、面倒な処理が必要になります。

例えば100%と150%の拡大率に設定された別モニター間をマウスのドラッグ操作でフォームを移動させた場合、マウスの移動距離がモニターによって変わるため、拡大率を取得して、補完する処理が必要になります。

2つめは、Windows11で充実した「スナップ機能」が使えなくなります。

この辺りは、タイトルバーを使えばWindowsが上手にやってくれるのでタイトルバーを消して別のコントロールで代用するのはWindows11では「無理ゲー」に近くなってきているようです。

WPFでタイトルバーをカスタマイズ

古くからある「WinFormsアプリ」では難しいと分かったので「WPFアプリ」に乗り換えるために、タイトルバーをカスタマイズしてみました。

「WinFormsアプリ」と比較して、かなりコード量が増えたので、正しい方法か分かり辛いですが、一応タイトルバーの色を変更して「閉じる」ボタンを追加ができました。

まずは「新しいプロジェクトの作成」で[WPF アプリケーション]を作っていきます。プロジェクト名や、.NETの選択は適当に設定します。

作成されたプロジェクトで[デバッグ|デバック実行]を選択します。

タイトルバーを備えた画面が表示されます。

[ソリューション エクスプローラー]などから[NuGet パッケージの管理]メニューを選択します。

WPFのイベント関係を取得するために[ReactiveProperty]をインストールしておきます。”ReactiveProperty.WPF” を検索して[インストール]をクリックするだけです。

[MainWindows.xaml]ファイルに、次のコードを追加します。

Style="{DynamicResource WindowStyle}"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:ri="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"

場所は下のイメージを参考にしてください。

タイトルバーのカスタマイズする部分を構成するためにソリューションに[新しい項目の追加]画面で[リソース ディクショナリ(WPF)]を検索して追加を行います。ファイル名は適当に入力しておきます。

ReactiveProperty同様に[MainWindows.xaml]ファイルに、次のコードを追加します。ファイル名は前の手順で追加した[リソース ディクショナリ(WPF)]に合わせてください。

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

場所は下のイメージを参考にしてください。

追加した[リソース ディクショナリ(WPF)]ファイルに次のコードを追加します。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style x:Key="SystemButton" TargetType="Button">
        <Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="IsTabStop" Value="False"/>
        <Setter Property="FontFamily" Value="Marlett"/>
        <Setter Property="FontSize" Value="20"/>
        <Setter Property="Width" Value="48"/>
        <Setter Property="Height" Value="32"/>
    </Style>

    <Style x:Key="SystemCloseButton" TargetType="Button" BasedOn="{StaticResource SystemButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border Name="HoverButtonBorder" BorderThickness="0">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>

                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="HoverButtonBorder" Property="Background" Value="#FF0000" />
                        </Trigger>
                    </ControlTemplate.Triggers>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="WindowStyle" TargetType="Window">
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome CornerRadius="10" GlassFrameThickness="0" ResizeBorderThickness="8"/>
            </Setter.Value>
        </Setter>
        <Setter Property="Foreground" Value="LightGray"/>
        <Setter Property="FontSize" Value="15"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Window">
                    <Border x:Name="border" BorderBrush="#000000" BorderThickness="1">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <Grid Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Top">
                                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
                                    <Button Content="r" ToolTip="閉じる" Command="{x:Static SystemCommands.CloseWindowCommand}" Style="{StaticResource SystemCloseButton}"/>
                                </StackPanel>
                            </Grid>
                            <Grid Grid.Row="1">
                                <Rectangle Fill="#909090"/>
                                <ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
                            </Grid>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

長いですが、閉じるボタンを追加したタイトルバーと、クライアント領域の部分をグレーで塗りつぶすように設定して、閉じるボタンはマウスホバーで背景色が赤くなるようにしています。

[閉じる]ボタンをクリックされた場合のイベントを受取るために、次のコードを[MainWindow.xaml]に追加します。

<Window.CommandBindings>
    <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" Executed="CloseWindow"/>
</Window.CommandBindings>

場所は下のイメージを参考にしてください。

最後に[MainWindow.xaml.cs]にアプリを終了するイベントプロシージャを追加します。

private void CloseWindow(object sender, ExecutedRoutedEventArgs e)
{
    // アプリを終了します。
    SystemCommands.CloseWindow(this);
}

実行すると、こんな感じ。タイトルバーが黒く塗りつぶされて右端に[閉じる]ボタンが表示されます。

マウスポインターを近づけると[閉じる]ボタンの背景が赤くなります。クリックすると、もちろん画面が閉じてアプリケーションが終了します。

無事にタイトルバーをカスタマイズすることができました。

まとめ

今回は短い記事ですが、WPFアプリのタイトルバーをカスタマイズして色変更と閉じるボタンの追加を行いました。

WinFormsアプリよりも少し手間がかかりますが、自由度も高くてxamlを編集することで、かなり幅広いカスタマイズができます。

C#のWPFアプリのタイトルバーをカスタマイズしたい場合の参考になれば幸いです。

スポンサーリンク

最後までご覧いただき、ありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です