当前位置: 首页 > news >正文

UWP 扩展/自定义标题栏的方法,一些概念和一些注意事项

在 Windows 10 的前几个版本中将页面内容扩展到标题栏上还算简单,主要是没什么坑。直到一些新控件的引入和一些外观设计趋势变化之后,扩展标题栏开始出现一些坑了。

本文将重温 UWP 自定义标题栏或者扩展标题栏的方法,但更重要的是解决一些坑。


本文内容

    • 扩展/自定义标题栏
    • 一些概念
    • 一些坑
      • 控件在标题栏区域无法交互
      • 更高的标题栏,或者被遮挡
      • 在什么时机调用?
    • 适配移动设备

扩展/自定义标题栏

要扩展标题栏,只需要拿到 CoreApplicationView 的实例,然后设置 TitleBarExtendViewIntoTitleBar 属性为 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 之间的区别 一文中提到过 CoreApplicationCoreWindowCoreDispatcher 之间的关系。继续借用那篇文章中的图:

UWP 创建应用视图

其中,Window 是对 CoreWindow 的封装,提供了更多与 XAML 相关的功能。这里的 ApplicationView 也是这样,是对 CoreApplication 的封装,提供了 XAML 相关的功能。

那篇文章中详细描述了这几个概念之间的关系和区别。考虑到阅读的一致性,我摘抄过来:

具体来说,CoreWindow 是与操作系统、与整个应用打交道的类型,提供了诸如窗口的尺寸、位置、输入状态等设置或调用;Window 是与应用内 UI 打交道的类型,比如可以设置窗口内显示的 UI,设置内部哪个控件属于标题栏,获取此窗口内的 Compositor。与之对应的,CoreApplicationView 是应用与操作系统交互,与窗口消息循环机制协同工作的类型,包含窗口客户区和非客户区设置;ApplicationView 也是与应用内 UI 打交道的类型,它可以使用 XAML 相关的类型对应用程序视图进行更方便的设置。

总结起来,CoreWindowCoreApplicationView 提供更加核心的操作系统或应用底层功能,而 WindowApplicationView 对前者进行了封装,使得我们能够使用 Windows.UI.Xaml 命名空间下的类型对窗口和应用视图进行控制。

于是,我们便能够理解为什么扩展标题栏和设置标题栏颜色会使用到两个不一样的类型了。

ExtendViewIntoTitleBar 是改变了窗口的客户区(Client Area)和非客户区(Non-client Area)组成,这是传统 Win32 编程中的概念,是更接近操作系统底层的概念。BackgroundColorButtonBackgroundColor 这里需要用到 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 标题栏 - 林德熙。


参考资料

  • Title bar customization - UWP app developer - Microsoft Docs

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • 图片点击放大,你的网页也能做到!
  • UWP 应用中 CoreApplication / Application, CoreWindow / Window 之间的区别
  • 使用 C# 代码创建快捷方式文件
  • 发布了一款库(或工具包),如何持续地编写更新日志(ChangeLog)?
  • Windows 无法删除文件夹 —— 访问被拒绝 / 因为目录不是空的
  • 如何精准地用打印机在贺卡或邀请函上打字
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • 使用 Postman 调试 ASP.NET Core 开发的 API
  • 只有你能 new 出来!.NET 隐藏构造函数的 n 种方法(Builder Pattern / 构造器模式)
  • UWP 中的各种文件路径(用户、缓存、漫游、安装……)
  • 使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅(解析篇)
  • 使用并解析 OPML 格式的订阅列表来转移自己的 RSS 订阅(概念篇)
  • csproj 文件中那个空的 NuGetPackageImportStamp 是干什么的?
  • C#/.NET 中 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 不同的执行效果和用法建议
  • WPF 中那些可跨线程访问的 DispatcherObject(WPF Free Threaded Dispatcher Object)
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • Django 博客开发教程 16 - 统计文章阅读量
  • ES10 特性的完整指南
  • JSDuck 与 AngularJS 融合技巧
  • Laravel5.4 Queues队列学习
  • Sass Day-01
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 回顾2016
  • 排序(1):冒泡排序
  • 浅谈web中前端模板引擎的使用
  • 设计模式 开闭原则
  • 使用Gradle第一次构建Java程序
  • 使用parted解决大于2T的磁盘分区
  • 收藏好这篇,别再只说“数据劫持”了
  • 新书推荐|Windows黑客编程技术详解
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • # 数论-逆元
  • (145)光线追踪距离场柔和阴影
  • (C++20) consteval立即函数
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (办公)springboot配置aop处理请求.
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)计算机毕业设计大学生兼职系统
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (一一四)第九章编程练习
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .chm格式文件如何阅读
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .NET 设计一套高性能的弱事件机制
  • .net 无限分类
  • .net和php怎么连接,php和apache之间如何连接