single.php

C# WinUI 3アプリでカスタマイズしたタイトルバーのクリックイベントを有効にする

C# WinUI 3アプリを作っていく途中で、躓いた部分を備忘録的に投稿します。今回はWinUI3プロジェクトでSetTitleBarでタイトルバーに検索ボックスやボタンを配置した際にマウスのイベントが取得できない場合の対処法です。

タイトルバーのクリックが無効になる仕様

プロジェクトの作成時に[WinUI3]を選択して作成したアプリ画面のタイトルバーにSetTitleBarメソッドで、Xaml内のコントロール(検索ボックスなど)を配置した場合にクリックイベントが取得できないのは仕様です。

タイトルバーに検索ボックスなどを配置する手順については別記事をご覧ください。

例えば、こんなXamlを作成して

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

  <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="Auto"/>
        <ColumnDefinition x:Name="ButtonColumn" Width="Auto"/>
        <ColumnDefinition x:Name="RightDragColumn" Width="*"/>
        <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
      </Grid.ColumnDefinitions>
      <Image x:Name="TitleBarIcon" Source="/Assets/StoreLogo.png" Grid.Column="1" Width="16" Height="16" Margin="8,0,0,0"/>
      <TextBlock x:Name="TitleTextBlock" Text="AppTitle" Style="{StaticResource CaptionTextBlockStyle}" Grid.Column="2" VerticalAlignment="Center" Margin="4,0,0,0"/>
      <AutoSuggestBox Grid.Column="4" QueryIcon="Find" PlaceholderText="検索" VerticalAlignment="Center" Width="160" Margin="4,0"/>
      <Button Grid.Column="5" Width="32" Height="32" Margin="0,0,0,0" Padding="0,0,0,0" Click="Button_Click" ClickMode="Press">
      <Image x:Name="IconButton" Source="/Assets/StoreLogo.png" Width="32" Height="32" />
      </Button>
    </Grid>
  </Grid>
   
</Window>

MainWindowのインスタンス作成時に次の様にSetTitleBarメソッドを使います。

ExtendsContentIntoTitleBar = true;
SetTitleBar(AppTitleBar);

実行時にアプリのタイトルバーが、次の様にカスタマイズできます。

しかし、キーボードの[tab]キーでフォーカスを移すことはできますが、マウスのクリックで検索バーにフォーカスを移したり、ボタンコントロールをクリックすることが出来ません。

これは、WinUI3を使ったタイトルバーのカスタマイズ時の制限(つまり仕様)のようで公式ページの「タイトル バーのカスタマイズ」には次のような記載があります。

SetTitleBar に渡される要素は、ドラッグ、ダブルクリックしてサイズを変更、右クリックしてウィンドウのコンテキスト メニューを表示するなど、標準のタイトル バーと同じシステム操作をサポートします。 その結果、すべてのポインター入力 (マウス、タッチ、ペンなど) がシステムによって処理されます。 タイトル バー要素とその子要素では認識されなくなりました。 タイトル バー要素によって占有される四角形の領域は、要素が別の要素によってブロックされている場合や、要素が透明である場合でも、ポインターの目的でタイトル バーとして機能します。 ただし、キーボード入力は認識され、子要素はキーボード フォーカスを受け取ることができます。

つまり、キーボード入力とフォーカスを使用する場合を除き、タイトル バー領域の要素を操作することはできません。 検出可能性とアクセシビリティの問題が発生するため、これはお勧めしません。

公式ページ[タイトル バーのカスタマイズ]の”対話型コンテンツ”より

つまり、本来のタイトルバーを透明にしたり別の要素などでトリックを使ってもタイトルバー部分の動作はシステムに処理が委ねられるため、マウスクリックによるフォーカスやドラッグによる移動以外の動作は無視される訳です。(他にも、キーボード入力は認識されるのも仕様)

OverlappedPresenterで一応回避可能

色々調べたところ、OverlappedPresenterクラスのSetBorderAndTitleBarメソッドで一応は、マウスによるフォーカス移動とクリックイベントを有効にすることが一応できました。

WinUI3は日々更新されていくので、2023年1月16日現在の最新版で有効です。また、一応と書いたのは、別で追加の処理が必要になったりするためです。

具体的には次の手順で、マウスで制御できるコントロールをタイトルバーに配置するカスタマイズができます。

次のコードをMainWindowのインスタンス部分に追加します。(Xamlは前に記載したコードから変更はありません)

private Microsoft.UI.Windowing.AppWindow m_AppWindow;

public MainWindow()
{
  this.InitializeComponent();

  IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
  Microsoft.UI.WindowId windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
  m_AppWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);

  var op = OverlappedPresenter.Create();
  op.SetBorderAndTitleBar(true, false);
  m_AppWindow.SetPresenter(op);
  ExtendsContentIntoTitleBar = true;
}

前段部分は、MainWindowのハンドルからウィンドウオブジェクトを取得して、その後にOverlappedPresenterクラスの、SetBorderAndTitleBarメソッドを使ってタイトルバーを消してます。

ビルド後に実行すると、こんな感じにウィンドウのタイトルバーがXaml内のAppTitleBarで定義したコントロールと融合して表示されます。

検索ボックスでマウスをクリックすると、フォーカスが移り入力モードに切り替わります。もちろん右側のボタンをクリックした際にイベントを取得することも可能になります。

ここまで順調ですが、出来なくなることもあります。

1つはタイトルバーをドラックしてウィンドウを移動させることが出来なくなります。(下の画像の赤い囲みの部分で、マウスのドラッグでウィンドウを移動させることができます)

2つめはシステムメニュー(下の画像のようなタイトルバーを右クリックして、表示される移動や最大化などが含まれたポップアップメニュー)

以上2つのデメリットがあるので、完全に対応できませんが、SetBorderAndTitleBarメソッドを使って、タイトルバーに追加したコントロールをマウスで操作できるようになります。

まとめ

今回は短い記事ですが、WinUI 3 プロジェクトで、Xaml上に定義したコントロールをタイトルバーに追加するSetTitleBarメソッドを実行した場合に、カスタマイズされたタイトルバーのマウスイベントが無効になります。

フォーカスの移動やクリックイベントが発生しなくなるのは、WinUI3の仕様でキーボードの入力しか受け付けなくなります。

SetTitleBarメソッドを利用せずに、OverlappedPresenter::SetBorderAndTitleBarメソッドを使うことで、出来ない部分がありますがマウス操作を受け付けるタイトルバーのカスタマイズを行うことができました。

C#のWinUI 3アプリで、SetTitleBarでタイトルバーのカスタマイズをした場合にマウスのイベントが取得されず困っている人の参考になれば幸いです。

スポンサーリンク

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

コメントを残す

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