当前位置:网站首页>Maui学习之路(三)--Winui3深入探讨
Maui学习之路(三)--Winui3深入探讨
2022-07-02 12:39:00 【dotNET跨平台】
Maui的学习之路 --- Winui3深入探讨
学习Maui
已经有一段时间,随着不断地深入,对Maui
有了一些初步的了解。
我们都知道Maui
为了保持平台原生特性,所以在每一个平台都使用了平台自身的原生开发框架,如在Windows
系统使用了Winui3
作为UI
框架,Mac
平台使用了UIKit
作为UI
框架,(我愿称之为“套娃”
或者是对不同平台做了一层“抽象”
),所以如果直接操作Maui
的对象的某个属性或者方法实际他背后去转调了平台相关的属性或者方法。
有了这样的认识那么我们就会深刻的理解到如果我想改变某些东西,其实也可以自己去直接操作平台相关的方法,而并不一定需要操作Maui
对象(如果Maui
未提供这样的能力,你只能这么做)。
在做Windows
桌面程序开发时,我们常常有这样的需求,你的exe
程序同一时间只能运行一份(单例),你很希望程序打开就全屏,别人不能轻易关闭(通常在工业领域这样的需求极大)。
基于以上需求,我们来深入了解学习一下,如何实现:
首先使用Maui
模板创建的每一个工程有有一个Platforms
的目录,这个本质上是对应多平台的一个工程集合(在Xamarin
中如果你需要构建一个真正的跨平台程序,实际是做不到的,你需要通过Xamarin.Form
的模板创建不同平台的入口,然后抽出公用的平台无关逻辑来作为跨平台的共享资源),有了这个设计Maui
真正实现了一份代码到处编译到处运行,而你在别的平台编译并不需要对工程做任何特别的改动(目前支持Mac
、Windows
平台编译)(这里不做深入探讨,如果你想了解Maui
和Xamarin
工程上的区别请看这个视频:.NET MAUI 跨平台开发合集_哔哩哔哩_bilibili[1])

其次对于Maui
的可执行工程(非dll
)来说,对应平台的程序的真正入口并非是MauiProgram
或Maui
的Application
,他实际是Platforms
下对应的平台的Main或者是App,比如对Window
来说,Maui
启动程序的真正入口是Platforms/Windows/App这个对象。

Window单例实现
有了上面的认识作为基础,那么实现一个单例非常简单,在Wpf
或者Winform
上做过相同的设计,如今只需要搬运过来即可(在Wpf
或是Winform
实现单例的方式很多,最常用的是使用Mutex
)实现方式请查阅:零食栏 - .NET MAUI Community Toolkit - .NET Community Toolkit | Microsoft Docs [2]在Platform/Windows/App.xaml.cs中增加单例检查
public partial class App : MauiWinUIApplication
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
static Mutex? __SingleMutex;
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
if (!IsSingleInstance())
{
//Process.GetCurrentProcess().Kill();
Environment.Exit(0);
return;
}
base.OnLaunched(args);
}
static bool IsSingleInstance()
{
const string applicationId = "813342EB-7796-4B13-98F1-14C99E778C6E";
__SingleMutex = new Mutex(false, applicationId);
GC.KeepAlive(__SingleMutex);
try
{
return __SingleMutex.WaitOne(0, false);
}
catch (Exception)
{
__SingleMutex.ReleaseMutex();
return __SingleMutex.WaitOne(0, false);
}
}
}
实现一个无边框窗体
在这之前我其实已经写过一篇Maui在windows上实现无边框的方式,有兴趣的同学可以去查阅一下(链接:Window窗体设置)。Maui
实际提供了一名为Window
的类型,不过很可惜这个类型中并没有任何有设置窗口相关的属性(比如:宽,高,背景色等等,另外也没法通过修改Style
来修改样式),很明显Maui
是一个跨平台的设计,因为在移动端并不存在所谓的窗口大小概念,通常打开都是全屏。如果我们需要对窗体进行修改,那么需要拿到Window
对象后面管理的Native对象才行
获取Native对象
方法1:注册Maui提供的生命周期函数
在不同的平台会推送相关的程序生命周期通知(在这里可以获取到平台相关操作对象包括对应的Application
,Window
),使用这个方式存在一些弊端就是当使用多窗体方案时,你没法定位哪个是主窗体(当然第一个创建的必然就是主窗体了),详情请看:应用生命周期 - .NET MAUI | Microsoft Docs[3](这里不做过多探讨如果你像知道细节可以看这个视频:.NET MAUI BLAZOR 生命周期_哔哩哔哩_bilibili[4])
方法2:访问Window下的Handler属性
Maui
Window
类型中存在这样一个属性Handler
(类型是IElementHandler
,实现类型是ElementHandler
)(所有的Maui
控件对象都存在这个个属性),该属性中记录了平台的Native映射对象(如果你希望修改native
对象的外观,可以访问他下属的属性PlatformView
(将其转换成对应平台的native
对象))(注意在刚开始new Window
对象时Handler
对象并不存在,你可以注册HandlerChanged
事件捕获他的变化)(在Winodw
平台PlatformView
对象是Microsoft.UI.Xaml.Window
类型)
学习必要的Winui3相关知识
从Winui
开始微软带来了窗口的全新设计,如果需要实现诸如窗口大小修改,标题栏修改,全屏实现等等功能你需要学习如下知识:
自定义标题栏
链接:标题栏自定义 - Windows apps | Microsoft Docs[5]
AppWindow
这是Win10
之后引入的窗口操作对象,学习链接:使用 AppWindow 类显示应用的辅助窗口 - Windows apps | Microsoft Docs[6]以及AppWindow Class (Microsoft.UI.Windowing) - Windows App SDK | Microsoft Docs[7]
注意:官网相关的学习资料在不同的文档中介绍存在偏差,主要是部分设计是老设计,尚未及时更新,请以Windows App SDK 1.1版本为准
通过学习以上知识,我们可以进行部分功能定制
更改窗口尺寸:
var winuiWindow = Window.Handler?.PlatformView as MicrosoftuiXaml.Window;
if (winuiWindow is null)
return;
var appWindow = winuiWindow.GetAppWindow();
if (appWindow is null)
return;
var displyArea = MicrosoftuiWindowing.DisplayArea.Primary;
double scalingFactor = winuiWindow.GetDisplayDensity();
var width = 800 * scalingFactor;
var height = 600 * scalingFactor;
double startX = (displyArea.WorkArea.Width - width) / 2.0;
double startY = (displyArea.WorkArea.Height - height) / 2.0;
appWindow.MoveAndResize(new((int)startX, (int)startY, (int)width, (int)height), displyArea);
最大化(使用Win32消息):
var winuiWindow = Window.Handler?.PlatformView as MicrosoftuiXaml.Window;
if (winuiWindow is null)
return;
var windowHanlde = winuiWindow.GetWindowHandle();
User32.PostMessage(windowHanlde, WindowMessage.WM_SYSCOMMAND, new IntPtr((int)SysCommands.SC_MINIMIZE), IntPtr.Zero);
最小化(使用Win32消息):
var winuiWindow = Window.Handler?.PlatformView as MicrosoftuiXaml.Window;
if (winuiWindow is null)
return;
var windowHanlde = winuiWindow.GetWindowHandle();
User32.PostMessage(windowHanlde, WindowMessage.WM_SYSCOMMAND, new IntPtr((int)SysCommands.SC_MINIMIZE), IntPtr.Zero);
全屏:
var winuiWindow = Window.Handler?.PlatformView as MicrosoftuiXaml.Window;
if (winuiWindow is null)
return;
var appWindow = winuiWindow.GetAppWindow();
if (appWindow is null)
return;
//注意由于Maui默认开启了扩展TitleBar(标题栏融合模式?)所以先要去掉 否则全屏仍然会出现 关闭等按钮
//虽然关闭了标题栏融合模式,但是全屏时仍然会存在一个类似标题栏的东西,如果需要处理需要进行深度定制(可以查看我的github项目)
winuiWindow.ExtendsContentIntoTitleBar = false;
appWindow.SetPresenter(MicrosoftuiWindowing.AppWindowPresenterKind.FullScreen);
修改
Maui
默认标题栏颜色:
var winuiWindow = Window.Handler?.PlatformView as MicrosoftuiXaml.Window;
if (winuiWindow is null)
return;
var application = MicrosoftuiXaml.Application.Current;
var res = application.Resources;
//看到这里你一定会疑惑为什么是这样,如果你有兴趣,可以查阅Winui3的源码
res["WindowCaptionBackground"] = new MicrosoftuixmlMedia.SolidColorBrush(Microsoftui.Colors.Red);
//修改标题栏后需要主动刷新才会生效(否则需要你人为进行一次最小化处理)
TriggertTitleBarRepaint();
以上Demo
都已经上传Github 地址:WPFDevelopersOrg/Demo[8] ,请查阅MauiApp1
这个demo
。
最后放一个目前我已经实现的Maui
Win11
的演示效果:
该项目已上传github地址:WPFDevelopersOrg/MauiToolkit[9]
参考资料
[1]
.NET MAUI 跨平台开发合集_哔哩哔哩_bilibili: https://www.bilibili.com/video/BV1VW4y1k7Bk?p=3
[2]零食栏 - .NET MAUI Community Toolkit - .NET Community Toolkit | Microsoft Docs : https://docs.microsoft.com/zh-cn/dotnet/communitytoolkit/maui/alerts/snackbar
[3]应用生命周期 - .NET MAUI | Microsoft Docs: https://docs.microsoft.com/zh-cn/dotnet/maui/fundamentals/app-lifecycle
[4].NET MAUI BLAZOR 生命周期_哔哩哔哩_bilibili: https://www.bilibili.com/video/BV1vA4y1d74A?spm_id_from=333.999.0.0
[5]标题栏自定义 - Windows apps | Microsoft Docs: https://docs.microsoft.com/zh-cn/windows/apps/develop/title-bar?tabs=winui3
[6]使用 AppWindow 类显示应用的辅助窗口 - Windows apps | Microsoft Docs: https://docs.microsoft.com/zh-cn/windows/apps/design/layout/app-window
[7]AppWindow Class (Microsoft.UI.Windowing) - Windows App SDK | Microsoft Docs: https://docs.microsoft.com/zh-CN/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.appwindow?view=windows-app-sdk-1.1
[8]WPFDevelopersOrg/Demo: https://github.com/WPFDevelopersOrg/Demo
[9]WPFDevelopersOrg/MauiToolkit: https://github.com/WPFDevelopersOrg/MauiToolkit
边栏推荐
- Introduction to database system Chapter 1 short answer questions - how was the final exam?
- 数组和链表的区别浅析
- Why does the system convert the temp environment variable to a short file name?
- 基于 Nebula Graph 构建百亿关系知识图谱实践
- SQL FOREIGN KEY
- The outline dimension function application of small motherboard
- In memory of becoming the first dayu200 tripartite demo contributor
- Solve the problem of base64encoder error
- /bin/ld: 找不到 -llz4
- Ant group's large-scale map computing system tugraph passed the national evaluation
猜你喜欢
Boot 事务使用
[development environment] install Visual Studio Ultimate 2013 development environment (download software | install software | run software)
《大学“电路分析基础”课程实验合集.实验四》丨线性电路特性的研究
Figure database | Nepal graph v3.1.0 performance report
Introduction to dynamic planning I, BFS of queue (70.121.279.200)
Recommended practice sharing of Zhilian recruitment based on Nebula graph
又是一年毕业季
Huawei ECS installs mysqlb for mysqld service failed because the control process exited with error code. See “sys
How to use percona tool to add fields to MySQL table after interruption
如何实现十亿级离线 CSV 导入 Nebula Graph
随机推荐
Fastjson list to jsonarray and jsonarray to list "suggested collections"
数据库系统概论第一章简答题-期末考得怎么样?
How to import a billion level offline CSV into Nepal graph
Traversal before, during and after binary tree
使用 percona 工具给 MySQL 表加字段中断后该如何操作
数字藏品系统开发(程序开发)丨数字藏品3D建模经济模式系统开发源码
Some problems about pytorch extension
Flink real-time data warehouse (IX): incremental synchronization of data in MySQL
又是一年毕业季
Experiment collection of University "Fundamentals of circuit analysis". Experiment 7 - Research on sinusoidal steady-state circuit
Locate: cannot execute stat() `/var/lib/mlocate/mlocate Db ': there is no such file or directory
Multi data source configuration code
【题解】Educational Codeforces Round 82
6092. Replace elements in the array
多数据源配置代码
【5G NR】RRC连接释放
Soul torture, what is AQS???
使用FFmpeg命令行进行UDP、RTP推流(H264、TS),ffplay接收
Conditions and solutions of deadlock
Huawei ECS installs mysqlb for mysqld service failed because the control process exited with error code. See “sys