原文 UWP 擴充/自定義标題欄的方法,一些概念和一些注意事項
在 Windows 10 的前幾個版本中将頁面内容擴充到标題欄上還算簡單,主要是沒什麼坑。直到一些新控件的引入和一些外觀設計趨勢變化之後,擴充标題欄開始出現一些坑了。
本文将重溫 UWP 自定義标題欄或者擴充标題欄的方法,但更重要的是解決一些坑。
本文内容
要擴充标題欄,隻需要拿到
CoreApplicationView
的執行個體,然後設定
TitleBar
的
ExtendViewIntoTitleBar
屬性為
true
即可。
var applicationView = CoreApplication.GetCurrentView();
applicationView.TitleBar.ExtendViewIntoTitleBar = true;
要自定義标題欄,隻需要拿到
ApplicationView
TitleBar
裡各種屬性接口。
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
titleBar.BackgroundColor = Colors.Khaki;
titleBar.ButtonBackgroundColor = Colors.Transparent;
那麼問題來了,為什麼前者需要拿到
CoreApplicationView
的執行個體,後者需要拿到
ApplicationView
的執行個體?它們到底是什麼差別?
我在
CoreApplication/Application、CoreWindow/Window 之間的差別一文中提到過
CoreApplication
、
CoreWindow
和
CoreDispatcher
之間的關系。繼續借用那篇文章中的圖:
其中,
Window
是對
CoreWindow
的封裝,提供了更多與 XAML 相關的功能。這裡的
ApplicationView
也是這樣,是對
CoreApplication
的封裝,提供了 XAML 相關的功能。
那篇文章中較長的描述了這幾個概念之間的關系和差別。考慮到閱讀的一緻性,我摘抄過來:
具體來說,是與作業系統、與整個應用打交道的類型,提供了諸如視窗的尺寸、位置、輸入狀态等設定或調用;
CoreWindow
是與應用内 UI 打交道的類型,比如可以設定視窗内顯示的 UI,設定内部哪個控件屬于标題欄,擷取此視窗内的
Window
。與之對應的,
Compositor
是應用與作業系統互動,與視窗消息循環機制協同工作的類型,包含視窗客戶區和非客戶區設定;
CoreApplicationView
ApplicationView
也是與應用内 UI 打交道的類型,它可以使用 XAML 相關的類型對應用程式視圖進行更友善的設定。
總結起來,
CoreWindow
提供更加核心的作業系統或應用底層功能,而
CoreApplicationView
Window
對前者進行了封裝,使得我們能夠使用
ApplicationView
命名空間下的類型對視窗和應用視圖進行控制。
Windows.UI.Xaml
于是,我們便能夠了解為什麼擴充标題欄和設定标題欄顔色會使用到兩個不一樣的類型了。
ExtendViewIntoTitleBar
是改變了視窗的客戶區(Client Area)和非客戶區(Non-client Area)組成,這是傳統 Win32 程式設計中的概念,是更接近作業系統底層的概念。
BackgroundColor
ButtonBackgroundColor
這裡需要用到
Windows.UI.Xaml
命名空間中的顔色,而
CoreApplicationView
太底層,無法使用 XAML 顔色。
想必當你擴充到标題欄後,在标題欄區域增加一些按鈕的時候,肯定會遇到下面的情況:
▲ 按鈕在标題欄區域的一半無法互動
這顯然是無法接受的。
然而,當我們将一個 XAML 控件指定為标題欄之後,就隻會是那個控件所在的區域響應标題欄操作,其他地方就會恢複正常。
// TitleBar 是我在 XAML 中寫的一個 x:Name="TitleBar" 的控件。
Window.Current.SetTitleBar(TitleBar);
▲ 按鈕在标題欄區域現在可以互動了
特别說明一下,
SetTitleBar
傳入的是
UIElement
類型的執行個體,也就是說這也是 XAML 互動的一部分。我們需要使用
Window
的執行個體,而不是
CoreWindow
的執行個體。
如果被指定為标題欄的控件更大,超出标題欄區域了,它還會成為标題欄嗎?如果被其他控件遮擋了,它還會響應标題欄事件嗎?
實際看來,無論它多大,都能響應标題欄事件;但被遮擋的部分就真的被遮擋了,沒有标題欄響應。
▲ 更高的标題欄,或者被遮擋
事實上,指定為标題欄的控件可以在界面的任何地方,不需要一定在頂部。隻不過,絕大多數不作死的應用都不會這樣設定吧!
擴充标題欄用的是
CoreApplicationView
,自定義标題欄顔色用的是
ApplicationView
,将控件指定為标題欄用的是
Window
。如果我們的應用隻有一個視圖,其實我們随便找一個初始化的地方調用就好了。但如果我們的應用有多個視圖,那麼給非主要視圖調用的時候就需要在其初始化之後了。閱讀
了解 UWP 視圖的概念,讓 UWP 應用顯示多個視窗(多視圖)了解如何編寫多個視圖的 UWP 應用,了解非主要視圖的初始化時機。
當然,如果你比較極客,從
Main
函數開始寫 UWP 應用,就像我在
為了了解 UWP 的啟動流程,我從零開始建立了一個 UWP 程式一文中做的一樣,那麼你也需要等到初始化完畢之後才能調用(至少是
SetWindow
之後了)。
移動裝置上并不是标題欄,而是狀态了和虛拟按鍵。關于擴充視圖到這些區域,可以閱讀
win10 uwp 标題欄 - 林德熙。
本文會經常更新,請閱讀原文:
https://walterlv.com/post/tips-for-customize-uwp-title-bar.html,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。