UWP - 操作 Title Bar

UWP 支援控制 Tile Bar 讓 App 有更多設計與操作的地方,該篇整理了一些開發上的筆記分享給大家。

代表目前 App 的 title bar,利用 ApplicationView.GetForCurrentView().TitleBar 取得,屬於 Windows.UI.ViewManagement。ApplicationView 代表目前執行 App 視窗與關聯的狀態/行爲。

Name Access Type Description
BackgroundColor / ForegroundColor Read/write Gets or sets the color of the title bar background / foreground.
InactiveBackgroundColor / InactiveForegroundColor Read/write Gets or sets the color of the title bar background / foreground when it's inactive.
ButtonBackgroundColor  / ButtonForegroundColor Read/write Gets or sets the background / foreground color of the title bar buttons.
ButtonInactiveBackgroundColor / ButtonInactiveForegroundColor Read/write Gets or sets the background / foreground color of a title bar button when it's inactive.
ButtonHoverBackgroundColor  / ButtonHoverForegroundColor Read/write Gets or sets the background / foreground color of a title bar button when the pointer is over it.
ButtonPressedBackgroundColor  / ButtonPressedForegroundColor Read/write Gets or sets the background / foreground color of a title bar button when it's pressed.

通常會設定會讓 Inactive 類型的顔色一致,這樣看起來比較好看;如果有改了 Background 也要記得一致改掉 ButtonBackground 不然會掉一塊色。

private void RadioButton_Click(object sender, RoutedEventArgs e)
{
    if (UseStandardColors.IsChecked.Value)
    {
	// 要設定爲 null 才會還原是用 系統預設
	titleBar.BackgroundColor = null;
	titleBar.ForegroundColor = null;
	titleBar.InactiveBackgroundColor = null;
	titleBar.InactiveForegroundColor = null;

	titleBar.ButtonBackgroundColor = null;
	titleBar.ButtonHoverBackgroundColor = null;
	titleBar.ButtonPressedBackgroundColor = null;
	titleBar.ButtonInactiveBackgroundColor = null;

	titleBar.ButtonForegroundColor = null;
	titleBar.ButtonHoverForegroundColor = null;
	titleBar.ButtonPressedForegroundColor = null;
	titleBar.ButtonInactiveForegroundColor = null;
    }
    else
    {
	// Title bar colors. Alpha must be 255.
	titleBar.BackgroundColor = new Color() { A = 255, R = 54, G = 60, B = 116 };
	titleBar.ForegroundColor = new Color() { A = 255, R = 232, G = 211, B = 162 };
	titleBar.InactiveBackgroundColor = new Color() { A = 255, R = 135, G = 141, B = 199 };
	titleBar.InactiveForegroundColor = new Color() { A = 255, R = 232, G = 211, B = 162 };
    }
}

*** 要注意設定顔色的時候, title bar 的 alpha 必須是 255 (FF);title bar 中 button 的 foreground 也必須是 255,衹有 button 的 background 才可以設定 alpha。 ***

 

上述介紹怎麽調整 Title Bar 的顔色,讓畫面設計上能夠更符合自己 App 的色系。如果說這樣的調整還不夠滿足,往下將説明怎麽自訂自己的 Title Bar。

代表目前視窗的 title bar。允許 app 在視窗裏自定義 title bar。CoreApplicationView 代表目前 App 執行的視窗與它的執行續,屬於 Windows.ApplicationModel.Core。

Type Name Description
Event IsVisibleChanged Occurs when the visibility of the title bar (indicated by the IsVisible property) changes.
  LayoutMetricsChanged Occurs when the title bar needs to respond to size changes.
The most common trigger for this event is when the app window moves to a screen that has a different DPI.
Use this event to verify and update the positioning of UI elements that depend on the title bar's size.
Property ExtendViewIntoTitleBar read/write. Gets or sets a value that specifies whether this title bar should replace the default window title bar.
  Height read-only. Gets or sets the height of the title bar.
  IsVisible read-only. Gets a value that specifies whether this title bar is visible.
  SystemOverlayLeftInset read-only. Gets the width of the system-reserved region of the upper-left corner of the app window.
This region is reserved when the current language is a right-to-left language.
  SystemOverlayRightInset read-only. Gets the width of the system-reserved region of the upper-right corner of the app window.
This region is reserved when the current language is a left-to-right language.

上述以 LayoutMetricsChanged,IsVisibleChanged,ExtendViewIntoTitleBar 這三個最常使用,例如:

  1. ExtendViewIntoTitleBar:設定是否支援自訂的 title bar。設定之後搭配 Window.Current.SetTitleBar 更新 title bar 内容。
  2. IsVisibleChanged:適用於支援全屏幕時要關掉或開啓,設定 IsVisible 后所觸發。
  3. ayoutMetricsChanged:適用於當用戶在 Desktop 下拉動視窗大小時所觸發,可用來設定顯示或開關 title bar 的内容。

參考範例説明:

1. 建立一個自定義的 TitleBar 跟載入 content 的 XAML

<Grid x:Name="rootGrid">
    <Grid.RowDefinitions>
	<RowDefinition Height="Auto" />
	<!-- row = 1 放内容的地方 -->
	<RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid Background="Yellow" Grid.Row="0" x:Name="TitleBar">
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="Auto"/>
		<ColumnDefinition/>
		<ColumnDefinition Width="Auto"/>
		<ColumnDefinition Width="Auto"/>
	</Grid.ColumnDefinitions>
	<Grid Background="Red" x:Name="BackButtonGrid">
		<Button x:Name="BackButton" />
	</Grid>
	<Grid Grid.Column="1" x:Name="MainTitleBar" Background="Transparent">
		<TextBlock Text="My custom title bar" VerticalAlignment="Center" FontSize="12" FontFamily="Segoe UI" FontWeight="Normal" Margin="10,0"></TextBlock>
	</Grid>
    </Grid>
</Grid>

2. 爲自定義的 Title bar 注冊 CoreApplicationViewTitleBar 的事件,并且設定給 Window.Current.SetTitleBar:

private void CustomTitleBar_Loaded(object sender, RoutedEventArgs e)
{
    // 注冊事件
    coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
    coreTitleBar.IsVisibleChanged -= TitleBar_IsVisibleChanged;
    coreTitleBar.IsVisibleChanged += TitleBar_IsVisibleChanged;
    coreTitleBar.LayoutMetricsChanged -= TitleBar_LayoutMetricsChanged;
    coreTitleBar.LayoutMetricsChanged += TitleBar_LayoutMetricsChanged;
}

private void TitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
{
    // 更新自定義 TitleBar 的高度
    if (TitleBar != null)
    {
	TitleBar.Height = sender.Height;
    }
}

private void TitleBar_IsVisibleChanged(CoreApplicationViewTitleBar sender, object args)
{
    // 設定自定義 TitleBar 是否要顯示
    if (TitleBar != null)
    {
	TitleBar.Visibility = sender.IsVisible ? Visibility.Visible : Visibility.Collapsed;
    }
}

public void SetContent(UIElement content)
{
    rootGrid.Children.Add(content);
    Grid.SetRow((FrameworkElement)content, 1);

    coreTitleBar.ExtendViewIntoTitleBar = true;
    Window.Current.SetTitleBar(TitleBar);
}

public void RemoveContent(UIElement content)
{
    rootGrid.Children.Remove((FrameworkElement)content);
    coreTitleBar.ExtendViewIntoTitleBar = false;
    Window.Current.SetTitleBar(null);
}

3. 更換 App 的 TitleBar:

private void ChangeToCustomTitleBar()
{
     if (customTitleBar== null)
     {
	customTitleBar = new CustomTitleBar();
     }

     if (UseExtenTitleBar.IsChecked.Value)
     {
	// 先將目前畫面的内容暫時存起來
	cacheContent = this.Content;
	this.Content = customTitleBar;
	// 設定給 custom title bar 并更新現在 mainpage 的 content
	customTitleBar.SetContent(cacheContent);
     }
     else
     {
	// 還原原本的 content 給 mainpage
	this.Content = cacheContent;
	// 移除 custom title bar 的 content
	customTitleBar.RemoveContent(cacheContent);
	cacheContent = null;
     }
}

上面的範例裏面有一段是在更換 Content 的内容,爲甚麽需要這個樣子,藉由下圖來説明:

可看出 Window.Current.SetTitleBar 之後整個畫面會往上提,所以需要保留一塊放 Title 的高度才能顯示正確。

更要特別注意的是,不要把整個 control 設定給 SetTitleBar 會造成原本 Content 的部分無法使用

上述以自訂 title bar 的範例,因爲是自定義的所以原本預設的 back 按鈕,這是需要自己控制的,可以參考<UWP - 處理 back-navigation event 機制>。

 

瞭解了 TitleBar 的自訂與調整后,接著說明一下觀念上面怎麽分別 ApplicationView 跟 CoreApplicationView 的差別。

代表應用程式 view 與相關的狀態或行爲。在 Windows 裏面可以同時有 4 個 window 呈現在屏幕中,它們同時顯示的寬度可變,不重疊,並且其頂部和底部邊緣觸控式螢幕幕的頂部和底部邊緣。連續的視窗之間可能存在非視窗區域。

ApplicationView 不等於 window,它代表的 App 本體顯示的 view

重要的屬性與事件,可以看出一些功能都是針對整個 App 的顯示在做控制,如下:

Type Name Description
Event Consolidated Occurs when the window is removed from the list of recently used apps, or if the user executes a close gesture on it.
Method ExitFullScreenMode Takes the app out of full-screen mode.
  GetApplicationViewIdForWindow Gets the window ID that corresponds to a specific CoreWindow managed by the app.
  GetForCurrentView Gets the view state and behavior settings of the active application.
  TryEnterFullScreenMode Attempts to place the app in full-screen mode.
Properties FullScreenSystemOverlayMode Gets or sets a value that indicates how an app in full-screen mode responds to edge swipe actions.
  IsFullScreen Gets a value that indicates whether the window touches both the left and right sides of the display.
  Orientation Gets the current orientation (landscape or portrait) of the window (app view) with respect to the display.
  IsFullScreenMode Gets a value that indicates whether the app is running in full-screen mode.
  IsOnLockScreen Gets whether the window (app view) is on the Windows lock screen.
  IsScreenCaptureEnabled Gets or sets whether screen capture is enabled for the window (app view).
  Title Gets or sets the displayed title of the window.
  TitleBar Gets the title bar of the app.

 

代表 app window 與它的 thread。開啓 App 之後會有一個 default 的 window,所以當使用 CoreApplication.GetCurrentView 時就可以拿到目前被啓用的 window。

可以搭配 CoreApplication.CreateNewView 建立新的 window。

重要的屬性與事件,如下:

Type Name Description
Event Activated Occurs when the view is activated.
  HostedViewClosing Indicates that the hosted view is closing. Provides an opportunity for hosted window scenarios to defer the tear down of the hosted view.
Property CoreWindow Read-only. Gets the app window associated with the current view.
  Dispatcher Read-only. Gets the event message dispatcher associated with the current view.
  IsComponent Read-only. Gets whether the app was launched as a component that is embedded in another app by calling the LaunchAsync method.
  IsHosted Read-only. Gets the value that indicates whether this app view is hosted or not.
  IsMain Read-only. Gets a value that indicates whether this app view is the main app view or not.
  TitleBar Read-only. Gets the title bar associated with the current view.

 

  • ApplicationView 與 CoreApplicationView 的差別: CoreApplicationView 控制的是 window,而 ApplicationView 控制的整個 App 的 view,而 view 裏面包括了多個 window

更多的説明可以參考<The differences between Window ApplicationView CoreApplicationView and relationship?>。

 

[範例程式]

/DotblogsSampleCode

 

[補充]

取得目前 App 當前啓動的視窗。 Window 本身不是一個 control,重要的屬性事件方法如下:

Type Name Description
Event Activated Occurs when the window has successfully been activated.
  Closed Occurs when the window has closed.
  SizeChanged Occurs when the app window has first rendered or has changed its rendering size.
  VisibilityChanged Occurs when the value of the Visible property changes.
Methods Activate Attempts to activate the application window by bringing it to the foreground and setting the input focus to it.
  Close Closes the application window.
Properties Bounds Read-only. Gets a Rect value containing the height and width of the application window in units of effective (view) pixels.
  Content Read-write. Gets or sets the visual root of an application window.
  CoreWindow Read-only. Gets an internal core object for the application window.
  Current Read-only. Gets the currently activated window for an application.
  Dispatcher Read-only. Gets the CoreDispatcher object for the Window, which is generally the CoreDispatcher for the UI thread.
  Visible Read-only. Gets a value that reports whether the window is visible.

Window.Content 代表目前 App 執行的主要畫面會在 OnLaunched 先設定好。搭配在 SizeChanged 或是 VisibilityChanged 注意畫面被調整大小或是最小化的事件。

 

{ThemeResource} markup extension 可以取得目前系統使用的相關資源,它與 {StaticResource} markup extension 最大的不同在於,ThemeResource 會在每次用戶調整系統主題時就會被更動,跟 StaticResource 是在 XAML 載入后就固定是不同的。我比較常使用的就是 SystemAccentColor 來更換 Title bar 的顔色。

<Grid Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Width="50" Height="50" 
              Background="{ThemeResource SystemAccentColor}" />

 

======

以上是分享在調整 Title bar 的一些心得。我自己在開發上面比較少會用到 custom title bar ,主要原因是那邊用戶的 touch, mouse 要移動并不方便,加上在 Mobile 版本裏面是沒有支援的(衹有 StatusBar 可以簡單控制 UI)。不過在 Desktop 上可能還有很多使用情境是我沒有想到的。希望這篇對大家有幫助。謝謝。

 

References