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