[UWP]占领标题栏
1. 前言每一个有理想的UWP应用都会打标题栏的主意,尤其当微软提供 将 Acrylic 扩展到标题栏 这个功能后,大部分Windows 10的原生应用都不乖了,纷纷占领了标题栏的一亩三分地。这篇博客将介绍在UWP中如何自定义标题栏。 2.示例代码UWP的限制很多,标题栏的自定义几乎全部内容集中在 这篇文档 里面。但只参考这篇文章做起来还不够顺手,我参考了微软开源的计算器应用中的 TitleBar 写了一个示例应用,可以在 这里 查看它的源码。我也把TitleBar实际应用到了我的 OnePomodoro 应用里面了。 3. 简单的颜色自定义如果只想简单地自定义标题栏的颜色可以通过ApplicationViewTitleBar,ApplicationViewTitleBar表示应用程序的标题栏,它提供了一些颜色属性用于控制标题栏的颜色,示例代码如下: // using Windows.UI.ViewManagement; var titleBar = ApplicationView.GetForCurrentView().TitleBar; // Set active window colors titleBar.ForegroundColor = Windows.UI.Colors.White; titleBar.BackgroundColor = Windows.UI.Colors.Green; titleBar.ButtonForegroundColor = Windows.UI.Colors.White; titleBar.ButtonBackgroundColor = Windows.UI.Colors.SeaGreen; titleBar.ButtonHoverForegroundColor = Windows.UI.Colors.White; titleBar.ButtonHoverBackgroundColor = Windows.UI.Colors.DarkSeaGreen; titleBar.ButtonPressedForegroundColor = Windows.UI.Colors.Gray; titleBar.ButtonPressedBackgroundColor = Windows.UI.Colors.LightGreen; // Set inactive window colors titleBar.InactiveForegroundColor = Windows.UI.Colors.Gray; titleBar.InactiveBackgroundColor = Windows.UI.Colors.SeaGreen; titleBar.ButtonInactiveForegroundColor = Windows.UI.Colors.Gray; titleBar.ButtonInactiveBackgroundColor = Windows.UI.Colors.SeaGreen; 有几点需要注意:
4. 将内容扩展到标题栏若要隐藏默认标题栏并将你的内容扩展到标题栏区域中,请将 CoreApplicationViewTitleBar.ExtendViewIntoTitleBar 属性设置为 true。CoreApplicationViewTitleBar允许应用定义在应用窗口中显示的自定义标题栏。示例代码如下: // using Windows.ApplicationModel.Core; // Hide default title bar. var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; coreTitleBar.ExtendViewIntoTitleBar = true; 5. 将内容扩展到标题栏时自定义标题按钮颜色将内容扩展到标题栏,标题按钮的颜色就变复杂了。因为应用内容的颜色可能和按钮的颜色冲突。这种情况下有几种方案,其中最简单的一种方案是写死为一个不会冲突的颜色,但切换主题时可能会让这些颜色出问题。计算器应用中订阅UISettings的ColorValuesChanged事件,动态地根据ThemeResources的值改变标题栏颜色,并且更进一步地考虑到使用高对比度主题的情况,所以订阅了AccessibilitySettings的HighContrastChanged事件: if (_accessibilitySettings.HighContrast) { // Reset to use default colors. applicationTitleBar.ButtonBackgroundColor = null; applicationTitleBar.ButtonForegroundColor = null; applicationTitleBar.ButtonInactiveBackgroundColor = null; applicationTitleBar.ButtonInactiveForegroundColor = null; applicationTitleBar.ButtonHoverBackgroundColor = null; applicationTitleBar.ButtonHoverForegroundColor = null; applicationTitleBar.ButtonPressedBackgroundColor = null; applicationTitleBar.ButtonPressedForegroundColor = null; } else { Color bgColor = Colors.Transparent; Color fgColor = ((SolidColorBrush)Application.Current.Resources["SystemControlPageTextBaseHighBrush"]).Color; Color inactivefgColor = ((SolidColorBrush)Application.Current.Resources["SystemControlForegroundChromeDisabledLowBrush"]).Color; Color hoverbgColor = ((SolidColorBrush)Application.Current.Resources["SystemControlBackgroundListLowBrush"]).Color; Color hoverfgColor = ((SolidColorBrush)Application.Current.Resources["SystemControlForegroundBaseHighBrush"]).Color; Color pressedbgColor = ((SolidColorBrush)Application.Current.Resources["SystemControlBackgroundListMediumBrush"]).Color; Color pressedfgColor = ((SolidColorBrush)Application.Current.Resources["SystemControlForegroundBaseHighBrush"]).Color; applicationTitleBar.ButtonBackgroundColor = bgColor; applicationTitleBar.ButtonForegroundColor = fgColor; applicationTitleBar.ButtonInactiveBackgroundColor = bgColor; applicationTitleBar.ButtonInactiveForegroundColor = inactivefgColor; applicationTitleBar.ButtonHoverBackgroundColor = hoverbgColor; applicationTitleBar.ButtonHoverForegroundColor = hoverfgColor; applicationTitleBar.ButtonPressedBackgroundColor = pressedbgColor; applicationTitleBar.ButtonPressedForegroundColor = pressedfgColor; } 这段代码中,当使用高对比度主题时将标题栏的按钮颜色还原成默认值,否则设置成ThemeResource中对应的颜色,运行效果如下: 但现在的UWP应用常常在Dark和Light主题之间反复横跳,而Application.Current.Resources只能拿到程序加载时的ThemeResource的值,所以这段代码在应用内的主题切换后无效。我暂时不清楚怎么在代码里拿到最新的ThemeResource,为解决这个问题只好让TitleBar自己在XAML中获取当前的ThemeResource,代码如下: <UserControl.Resources> <SolidColorBrush x:Key="ButtonForegroundColor" Color="{ThemeResource SystemBaseHighColor}" /> <SolidColorBrush x:Key="ButtonInactiveForegroundBrush" Color="{ThemeResource SystemChromeDisabledLowColor}" /> <SolidColorBrush x:Key="ButtonHoverBackgroundBrush" Color="{ThemeResource SystemListLowColor}" /> <SolidColorBrush x:Key="ButtonHoverForegroundBrush" Color="{ThemeResource SystemBaseHighColor}" /> <SolidColorBrush x:Key="ButtonPressedBackgroundBrush" Color="{ThemeResource SystemListMediumColor}" /> <SolidColorBrush x:Key="ButtonPressedForegroundBrush" Color="{ThemeResource SystemBaseHighColor}" /> </UserControl.Resources> Color fgColor = ((SolidColorBrush)Resources["ButtonForegroundColor"]).Color; Color inactivefgColor = ((SolidColorBrush)Resources["ButtonInactiveForegroundBrush"]).Color; Color hoverbgColor = ((SolidColorBrush)Resources["ButtonHoverBackgroundBrush"]).Color; Color hoverfgColor = ((SolidColorBrush)Resources["ButtonHoverForegroundBrush"]).Color; Color pressedbgColor = ((SolidColorBrush)Resources["ButtonPressedBackgroundBrush"]).Color; Color pressedfgColor = ((SolidColorBrush)Resources["ButtonPressedForegroundBrush"]).Color; 6. 可拖动区域都将内容扩展到标题栏了,肯定是想在标题栏上放置自己需要的UI元素,默认情况下标题栏的范围为拖动、点击等Windows的窗体行为保留,在这个范围的自定义UI内容没办法获取鼠标点击。 为了让自定义的UI内容获取鼠标,可以用Window.SetTitleBar方法指定某一元素能用于窗体的拖动和点击。 <Grid x:Name="LayoutRoot" Height="32" HorizontalAlignment="Stretch"> <Grid x:Name="BackgroundElement" Height="32" Background="Transparent" /> <StackPanel Orientation="Horizontal"> <StackPanel x:Name="ItemsPanel" Orientation="Horizontal"> </StackPanel> <TextBlock x:Name="AppName" x:Uid="AppName" Text="ExtendViewIntoTitleBarDemo" </StackPanel> </Grid> Window.Current.SetTitleBar(BackgroundElement); 上面的代码指定TitlaBar中的 7. 标题的系统保留区域标题栏的右边有188像素的系统保留区域,用于系统标题按钮(“后退”、“最小化”、“最大化”、“关闭”)。其实这几个按钮也就占用了141像素的控件,还有一小块空间是默认的可拖动区域,这小块空间确保了无论怎么设置都总有一个用户可拖动的区域。 上面说的188像素是100%缩放的情况,通过上面的截图可以看到实际上可能不一样,通常来说会在窗体加载时,或者订阅CoreApplicationViewTitleBar.LayoutMetricsChanged事件,然后通过 _coreTitleBar.LayoutMetricsChanged += OnLayoutMetricsChanged; private void OnLayoutMetricsChanged(CoreApplicationViewTitleBar sender,object args) { LayoutRoot.Height = _coreTitleBar.Height; SetTitleBarPadding(); } private void SetTitleBarPadding() { double leftAddition = 0; double rightAddition = 0; if (FlowDirection == FlowDirection.LeftToRight) { leftAddition = _coreTitleBar.SystemOverlayLeftInset; rightAddition = _coreTitleBar.SystemOverlayRightInset; } else { leftAddition = _coreTitleBar.SystemOverlayRightInset; rightAddition = _coreTitleBar.SystemOverlayLeftInset; } LayoutRoot.Padding = new Thickness(leftAddition,rightAddition,0); } 8. 可交互区域的内容上面的 <StackPanel Orientation="Horizontal"> <StackPanel x:Name="ItemsPanel" Orientation="Horizontal"> <StackPanel.Resources> <Style TargetType="Button" BasedOn="{StaticResource NavigationBackButtonNormalStyle}"> <Setter Property="Foreground" Value="{StaticResource TitleBarForeground}" /> <Setter Property="FontSize" Value="10" /> <Setter Property="Width" Value="46" /> <Setter Property="Height" Value="32" /> <Setter Property="IsTabStop" Value="False" /> </Style> </StackPanel.Resources> </StackPanel> <TextBlock x:Name="AppName" x:Uid="AppName" Text="ExtendViewIntoTitleBarDemo" Margin="12,12,0" HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="{ThemeResource SystemControlPageTextBaseHighBrush}" FontSize="12" IsHitTestVisible="False" TextAlignment="Left" TextTrimming="CharacterEllipsis" /> </StackPanel> 其中 public ObservableCollection<Button> Buttons { get; } = new ObservableCollection<Button>(); private void OnButtonsCollectionChanged(object sender,System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { ItemsPanel.Children.Clear(); foreach (var button in Buttons) { ItemsPanel.Children.Add(button); } } <local:TitleBar> <local:TitleBar.Buttons> <Button x:Name="OptionsButton" Content="" ToolTipService.ToolTip="Options" /> <Button Content="" ToolTipService.ToolTip="Options" /> <Button Content="" ToolTipService.ToolTip="Options" /> <Button Content="" ToolTipService.ToolTip="Options" /> </local:TitleBar.Buttons> </local:TitleBar> 按钮的样式来自 9. 非激活状态的标题栏颜色当窗体处于非激活状态应该让按钮和标题都变灰,可以订阅 Window.Current.Activated += OnWindowActivated; private void OnWindowActivated(Object sender,WindowActivatedEventArgs e) { VisualStateManager.GoToState( this,e.WindowActivationState == CoreWindowActivationState.Deactivated ? WindowNotFocused.Name : WindowFocused.Name,false); } <UserControl.Resources> <SolidColorBrush x:Key="TitleBarForeground" x:Name="TitleBarForeground" Color="{ThemeResource SystemBaseHighColor}" /> </UserControl.Resources> <Grid x:Name="LayoutRoot" Height="32" HorizontalAlignment="Stretch"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="WindowFocusStates"> <VisualState x:Name="WindowFocused" /> <VisualState x:Name="WindowNotFocused"> <VisualState.Setters> <Setter Target="AppName.Foreground" Value="{ThemeResource SystemControlForegroundChromeDisabledLowBrush}" /> <Setter Target="TitleBarForeground.Color" Value="{ThemeResource SystemChromeDisabledLowColor}" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> 10. 全屏和平板模式当应用在全屏或平板模式下运行时,系统将隐藏标题栏和标题控制按钮。 但是,用户可以调用标题栏,以使其以覆盖形式显示在应用的 UI 顶部。 你可以处理隐藏或调用标题栏时将通知的 CoreApplicationViewTitleBar.IsVisibleChanged 事件,并根据需要显示或隐藏你的自定义标题栏内容。 LayoutRoot.Visibility = _coreTitleBar.IsVisible ? Visibility.Visible : Visibility.Collapsed; 这部分比较难截图就不搞了,想看效果可以试玩我的番茄钟应用。 11.结语就这样,令人头痛的自定义标题栏处理完了。还好微软开源了它的计算器里正好有我需要的代码,抄了个爽。有一些处理得不好,如果错误请指正。 12.参考标题栏自定义 calculator_TitleBar.xaml.cpp at master ApplicationViewTitleBar Class (Windows.UI.ViewManagement) - Windows UWP applications Microsoft Docs CoreApplicationViewTitleBar Class (Windows.ApplicationModel.Core) - Windows UWP applications Microsoft Docs 13. 源码DinoChan_ExtendViewIntoTitleBarDemo How to handle titlebar when ExtendViewIntoTitleBar OnePomodoro_TitleBar.xaml at master (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- windows-server-2008 – 同步域控制器时钟
- 强制.NET互操作使用本地COM DLL
- windows – 符合MSSCCI标准的git客户端
- windows-server-2012 – Apache随机停止提供HTTPS,但HTTP工
- Windows的默认PATH系统变量?
- windows-server-2008-r2 – NetApp FAS 2040 LDAP Win2k8R2
- 如何知道Windows中的网络是否启用了IP多播?
- WPF-上班合理摸鱼系列-第一章:WPF筑基期(三十年河东三十年河
- windows-7 – InnoSetup – 编译签名工具失败,退出代码为0x
- 窗口 – 管道输出(stdout)从运行进程Win32Api
- .net – 如何重新加载经常崩溃的第三方DLL
- windows-server-2008-r2 – 将Hyper-V集群升级到
- APC注入
- 如何通过MSI升级安装程序WIX bootstrapper bundl
- Microsoft SQL Server(MSSQL)可更新视图,具有多个
- active-directory – 如何将时间源从“本地CMOS时
- windows-server-2008 – 在与RDP断开连接时从服务
- windows-phone-7 – 我们如何在Windows Phone 7中
- windows 系统下python 模块安装
- windows-7 – 什么样的基于Windows的“进程记帐”