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;

有几点需要注意:

  • 悬停和按下状态的Background定义对关闭按钮无效
  • Foreground不能设置透明

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中的BackgroundElement元素为可拖动区域,而下面的StackPanel则用于放置交互内容,例如标题或后退按钮。这个StackPanel必须比BackgroundElement具有较高的Z顺序才能接收到用户的鼠标输入。

7. 标题的系统保留区域

标题栏的右边有188像素的系统保留区域,用于系统标题按钮(“后退”、“最小化”、“最大化”、“关闭”)。其实这几个按钮也就占用了141像素的控件,还有一小块空间是默认的可拖动区域,这小块空间确保了无论怎么设置都总有一个用户可拖动的区域。

上面说的188像素是100%缩放的情况,通过上面的截图可以看到实际上可能不一样,通常来说会在窗体加载时,或者订阅CoreApplicationViewTitleBar.LayoutMetricsChanged事件,然后通过CoreApplicationViewTitleBar获取具体的值。

_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, 0, rightAddition, 0);
}

8. 可交互区域的内容

上面的StackPanel是可交互区域,详细的内容如下:

<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,0,12,0"HorizontalAlignment="Left"VerticalAlignment="Center"Foreground="{ThemeResource SystemControlPageTextBaseHighBrush}"FontSize="12"IsHitTestVisible="False"TextAlignment="Left"TextTrimming="CharacterEllipsis" />
</StackPanel>

其中AppName用于显示标题栏,ItemsPanel用于放其它按钮。TitleBar里定义了Buttons属性,调用TitleBar可以通过Buttons属性指定按钮(这部分代码我凌晨两点写的,写得十分敷衍,但写完又懒得改了)。

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>

按钮的样式来自NavigationBackButtonNormalStyle并稍作修改,大致上做到和标准的标题栏按钮一样。

9. 非激活状态的标题栏颜色

当窗体处于非激活状态应该让按钮和标题都变灰,可以订阅Window的Activated事件,在非激活状态时改变颜色:

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

uwp 获取listviewitem里的控件_[UWP]占领标题栏相关推荐

  1. UWP 查找模板中的控件

    UWP 查找模板中的控件 原文:UWP 查找模板中的控件 这个标题我也不知道咋起,意思说一下你就明白. 1. 对官方控件的模板进行定制修改,以满足多样化需求,还有漂亮的UI 比如ListView,Gr ...

  2. javascript获取asp.net服务器端控件的值(2009-10-31 15:24:26)转载标签:杂谈 分类:技术分类

    javascript获取asp.net服务器端控件的值 (2009-10-31 15:24:26) 转载 标签: 杂谈 分类:技术分类 代码如下: <%@ Page Language=" ...

  3. javascript获取asp.net服务器端控件的值

    代码如下: <%@ Page Language="C#" CodeFile="A.aspx.cs" Inherits="OrderManage_ ...

  4. [代码]获取源页的控件值

    重点总结 在ASP.NET中,要想从目标页获取源页的数据信息,可以使用两种方法: 一是通过获取源页的控件进而获取控件的属性值. 二是直接通过获取源页公开的公共属性来获取源页数据. 本示例代码则演示如何 ...

  5. Android Include的使用,获取include 里面的控件

    include 就是在一个布局中引入另一个布局,include 可以使相同的页面就写一次,提高了共同布局的复用性. 1.先定义一个共用的布局 <?xml version="1.0&qu ...

  6. Js获取file上传控件的文件路径总结

    总结一个获取file上传控件文件路径的方法 firefox由于保护机制只有文件名,不能获取完整路径. document.getElementById('file').onchange = functi ...

  7. mfc中在vector里添加控件类型的数据时出现C2248错误

    @mfc中在vector里添加控件类型的数据时出现C2248错误 这是我在.h文件中定义的2个容器 public:vector<CComboBox*> cbx;vector <CSt ...

  8. CADEditorX新控件_可进君羊交流与学习

    CADEditorX是一个ActiveX组件,用于在支持ActiveX和COM技术的任何开发环境中(例如C#,Visual C ++,Delphi,VB,JavaScript等)将CAD功能添加到网页 ...

  9. C#获取指定controlName的控件;遍历控件,反射控件类型等

    一般情况下或许用不到,但是也有特殊情况下会需要使用类似的功能. 1.比如你的软件做了多个界面版本,有中文版,英文版等等,就需要控制界面显示.通常会把控件名称与显示文字以键值对的形式存储到外部数据库或者 ...

最新文章

  1. xcode修改时间后就要重新编译_iOS 微信编译速度优化分享
  2. 程序员,如何三十而立?
  3. MyBatis-Plus selectMapsPage报错
  4. matlab 凹盘,刹车盘凹槽是怎么形成的
  5. OSI七层、TCP/IP五层、UDP、TCP的socket编程(服务端及客户端)、字节序转换、多进程以及多线程服务端的实现
  6. 2018年10月底新公司
  7. SHA256算法详解及python实现
  8. Maven下载安装与配置IDEA
  9. html制作简单扫雷,JavaScript制作windows经典扫雷小游戏
  10. 又拍云推出区块链开发平台,覆盖多个公链
  11. php ip138获取,php通过调用ip138数据库获取IP及网络类型
  12. 倪光南院士2006年9月1号在南京软件博览会上的讲话
  13. python 绘制频数与正太分布图
  14. 逻辑思维能力选择题30道
  15. Unity录音并保存成wav文件或者byte[]字节数组
  16. 两篇励志的文章[转]
  17. P1071,OpenJudge1.7-11潜伏者详解
  18. 程序员如何提高自制力
  19. 超级浏览器适合TikTok Shop使用吗?TikTok Shop群店如何管理?
  20. 配置java运行时环境时出现could not find java.dll

热门文章

  1. PyCharm LicenseServer 破解
  2. 将asp.net1.1的应用程序升级到asp.net2.0的一点心得
  3. 2022年春运火车票明起开卖
  4. QQ音乐全新上线HiRes高解析音质 听歌体验再升级
  5. 苹果第二代自研M系列芯片MacBook Pro有望在未来几周上市
  6. 阿里、腾讯隔空“对话”互联互通 打破垄断让中小商户受益是核心命题
  7. 亚马逊新任CEO本周一上任 未来十年将获价值2.1亿美元公司股票
  8. 5月首批国产游戏版号下发:又一大波游戏要来了
  9. 库克:iPhone 12更新换代用户数达到顶峰
  10. Redmi Note10系列发布时间曝光:最高搭载1亿像素主摄