モノトーンの伝説日記

OBS Studio と Blackmagic Design が大好き。

UWP でタイトル バーも含めたデザインを作成する

※ 将来的にはここに書いてあるライブラリー類が WinRT Library に含まれる予定ですが現在試行錯誤の段階のため直接コードを記述します。なお、ここに書いてあるコードは MIT License です。

 UWP でタイトルバーも含めてきれいに設計する方法を紹介します。

概要

  1. タイトル バーとコンテンツ領域の色を統一する (シンプル)
  2. (本題) 複数の状態によって色を統一する

1. タイトル バーとコンテンツ領域の色を統一する (シンプル)

 これだけなら簡単ですね。コンテンツ領域がシンプル (同一色で延長) する場合、タイトル バーの色指定 API を使うだけで実現できます。

var titleBar = ApplicationView.GetForCurrentView().TitleBar;
titleBar.ForegroundColor = this.ForegroundColor;
titleBar.BackgroundColor = this.BackgroundColor;
titleBar.InactiveForegroundColor = this.InactiveForegroundColor;
titleBar.InactiveBackgroundColor = this.InactiveBackgroundColor;
titleBar.ButtonForegroundColor = this.ButtonForegroundColor;
titleBar.ButtonBackgroundColor = this.ButtonBackgroundColor;
titleBar.ButtonHoverForegroundColor = this.ButtonHoverForegroundColor;
titleBar.ButtonHoverBackgroundColor = this.ButtonHoverBackgroundColor;
titleBar.ButtonPressedForegroundColor = this.ButtonPressedForegroundColor;
titleBar.ButtonPressedBackgroundColor = this.ButtonPressedBackgroundColor;
titleBar.ButtonInactiveForegroundColor = this.ButtonInactiveForegroundColor;
titleBar.ButtonInactiveBackgroundColor = this.ButtonInactiveBackgroundColor;

 とってもシンプル。xaml で使いたい人は私の

github.com

を使ってください。

2. (本題) 複数の状態によって色を統一する

 たとえば、2 カラムで縦領域が狭いとき、コンテンツ領域をできるだけ多くとるためナビゲーション バーを上に置くのではなく、左に置く場合、コンテンツ領域の背景がそのまま伸びてくることになります。画像で見てもらうとわかりやすいと思います。

 ツイート内容は気にしないでください。縦領域が十分なときはナビゲーション バーにアカウント表示があるのに対し、縦領域が狭くなっているとアカウント表示を消し、代わりに左下のコマンド バーにそれらの機能が移動されます。これは電話向けの横画面での利用に適した状態になっています。

 このような場合、次のような操作が必要です。

  1. タイトル バーまでコンテンツを拡張する。
  2. タイトル バーのボタンの背景を VSM を使って動的に指定を変更する必要がある
  3. タイトル バーの表示非表示をサポートしなければならない。

というような色々めんどいことがあります… なので、上記 1 で済むならそれに越したことはないんですが…

 自分の場合、Page を継承して ExtendViewPage を作り xaml Binding で参照できるように TemplateSettings (WinRTLibrary に入れるときは名前変えるかも) を作っています。

using Windows.ApplicationModel.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Mntone.IkaConnect.Views.Infrastructures
{
    public class ExtendViewPage : Page
    {
        public ExtendViewPage()
        {
            this.TemplateSettings = new ExtendViewIntoTitleBarTemplateSettings();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = this.ExtendViewIntoTitleBar;
            base.OnNavigatedTo(e);
        }

        protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
        {
            CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = false;
            base.OnNavigatingFrom(e);
        }

        public bool ExtendViewIntoTitleBar
        {
            get { return (bool)base.GetValue(ExtendViewIntoTitleBarProperty); }
            set { base.SetValue(ExtendViewIntoTitleBarProperty, value); }
        }
        public static readonly DependencyProperty ExtendViewIntoTitleBarProperty
            = DependencyProperty.Register(nameof(ExtendViewIntoTitleBar), typeof(bool), typeof(ExtendViewPage), PropertyMetadata.Create(true, OnExtendViewIntoTitleBarPropertyChanged));


        public ExtendViewIntoTitleBarTemplateSettings TemplateSettings
        {
            get { return (ExtendViewIntoTitleBarTemplateSettings)base.GetValue(TemplateSettingsProperty); }
            private set { base.SetValue(TemplateSettingsProperty, value); }
        }
        private static readonly DependencyProperty TemplateSettingsProperty
            = DependencyProperty.Register(nameof(TemplateSettings), typeof(ExtendViewIntoTitleBarTemplateSettings), typeof(ExtendViewPage), new PropertyMetadata(null));

        private static void OnExtendViewIntoTitleBarPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = ((ExtendViewPage)d).ExtendViewIntoTitleBar;
        }
    }
}
using Windows.ApplicationModel.Core;
using Windows.UI.Xaml;

namespace Mntone.IkaConnect.Views.Infrastructures
{
    public sealed class ExtendViewIntoTitleBarTemplateSettings : DependencyObject
    {
        private readonly CoreApplicationViewTitleBar _titlebar;

        internal ExtendViewIntoTitleBarTemplateSettings()
        {
            this._titlebar = CoreApplication.GetCurrentView().TitleBar;
            this.Update();
            this._titlebar.IsVisibleChanged += (s, e) => this.Update();
            this._titlebar.LayoutMetricsChanged += (s, e) => this.Update();
        }

        private void Update()
        {
            this.IsVisible = this._titlebar.IsVisible;
            this.Height = this._titlebar.Height;
            this.SystemOverlayLeftInset = this._titlebar.SystemOverlayLeftInset;
            this.SystemOverlayRightInset = this._titlebar.SystemOverlayRightInset;
            this.ContentMargin = new Thickness(
                this._titlebar.SystemOverlayLeftInset,
                0.0,
                this._titlebar.SystemOverlayRightInset,
                0.0);
        }

        public bool IsVisible
        {
            get { return (bool)base.GetValue(IsVisibleProperty); }
            private set { base.SetValue(IsVisibleProperty, value); }
        }
        private static readonly DependencyProperty IsVisibleProperty
            = DependencyProperty.Register(nameof(IsVisible), typeof(bool), typeof(ExtendViewIntoTitleBarTemplateSettings), PropertyMetadata.Create(false));

        public double Height
        {
            get { return (double)base.GetValue(HeightProperty); }
            private set { base.SetValue(HeightProperty, value); }
        }
        private static readonly DependencyProperty HeightProperty
            = DependencyProperty.Register(nameof(Height), typeof(double), typeof(ExtendViewIntoTitleBarTemplateSettings), PropertyMetadata.Create(0.0));

        public double SystemOverlayLeftInset
        {
            get { return (double)base.GetValue(SystemOverlayLeftInsetProperty); }
            private set { base.SetValue(SystemOverlayLeftInsetProperty, value); }
        }
        private static readonly DependencyProperty SystemOverlayLeftInsetProperty
            = DependencyProperty.Register(nameof(SystemOverlayLeftInset), typeof(double), typeof(ExtendViewIntoTitleBarTemplateSettings), PropertyMetadata.Create(0.0));

        public double SystemOverlayRightInset
        {
            get { return (double)base.GetValue(SystemOverlayRightInsetProperty); }
            private set { base.SetValue(SystemOverlayRightInsetProperty, value); }
        }
        private static readonly DependencyProperty SystemOverlayRightInsetProperty
            = DependencyProperty.Register(nameof(SystemOverlayRightInset), typeof(double), typeof(ExtendViewIntoTitleBarTemplateSettings), PropertyMetadata.Create(0.0));

        public Thickness ContentMargin
        {
            get { return (Thickness)base.GetValue(ContentMarginProperty); }
            set { base.SetValue(ContentMarginProperty, value); }
        }
        private static readonly DependencyProperty ContentMarginProperty
            = DependencyProperty.Register(nameof(ContentMargin), typeof(Thickness), typeof(ExtendViewIntoTitleBarTemplateSettings), PropertyMetadata.Create(new Thickness(0.0)));
    }
}

 お手軽ですが、これを使えば xaml から簡単に使えるのでいい感じですね。xaml はたぶんこの記事を閲覧してる方々のほうがよく知ってると思うので特に書きませんが、タイトル バーの FontSize は 12、Margin は左に 12 を指定するといい感じになることをお伝えしておきます。

まとめ

 タイトル バーまで使っているアプリとしては LINE がありますが、個人的にタイトル バーまで UI の clickable 要素を拡張するのはどうかと思う(少なくとも Edge でタブをいっぱい出している場合の操作感はあまりいいと思いません)ので、このようにしています。ただ、情報を表示するのには有用かと思いますので、たとえば、タイトルの右上に通知件数を表示するとかいろいろな応用はできると思うので、よかったら使ってみてください。

 以上です。