什么是MAUI
.NET Multi-platform App UI (MAUI) 的前身是Xamarin.Forms(適用於Android,iOS和UWP的跨平台移動優先框架),.NET MAUI是Xamarin.Forms的演進。我們擁有7年的為客戶提供技術支持的經驗,服務對象從獨立開發人員到一些全球性的大公司,我們正在改善產品的核心功能,加快UI渲染,投資研發一致的系統設計模式,並從移動端擴展到桌面端。
與WinUI的區別
MAUI是一個跨iOS、Android、Windows、MacOs多設備多生態的應用框架,而WinUI僅限於Windows。
面向用戶
.NET MAUI is for developers who want to:
- Write cross-platform apps in XAML and C#, from a single shared code-base in Visual Studio.
- Share UI layout and design across platforms.
- Share code, test, and business logic across platforms.
工作原理
.NET MAUI將Android、iOS、macOS和WindowsAPI統一為單個API,允許在任何地方開發人員處進行筆寫體驗,同時提供對每個本地平台各個方面的深度訪問。
.NET 6為創建應用提供了一系列特定於平台的框架:.Android的.NET、iOS的.NET、macOS的.NET和WindowsUI(WinUI)庫。這些框架都可以訪問相同的.NET 6基礎類庫(BCL)。此庫將基礎平台的詳細信息從您的代碼中摘要出來。BCL取決於.NET運行時間為您的代碼提供執行環境。對於安卓、iOS和macOS,環境由單聲道實施,這是.NET運行時間的實現。在Windows上,WinRT執行相同的角色,但窗口平台已優化除外。
雖然BCL使在不同平台上運行的應用能夠共享共同的商業邏輯,但不同的平台具有定義應用用戶界面的不同方式,它們為指定用戶界面元素的溝通和互操作方式提供了不同的模型。您可以使用適當的平台特定框架(.Android的NET、iOS的.NET、macOS的.NET或WinUI)分別為每個平台制作UI,但此方法需要您為每個設備系列維護代碼基礎。
.NET MAUI為為移動和桌面應用構建UI提供了單一框架。下圖顯示了.NETMAUI應用程序的架構的高層視圖:
在.NET MAUI應用中,您編寫的代碼主要與NET MAUI API交互。.NET MAUI然后直接消耗原生平台API。此外,如果需要,應用代碼可以直接執行平台API。
.NET MAUI應用可以寫在PC或Mac上,並編譯為原生應用包:
- 使用.NET MAUI構建的Android應用從C#編譯為中間語言(IL),然后在應用啟動時及時(JIT)編譯到本地裝配中。
- 使用.NET MAUI構建的iOS應用完全提前從C#編譯為原生ARM裝配代碼。
- 使用.NET MAUI構建的macOS應用使用MacCatalyst,這是蘋果的解決方案,將使用UIKit構建的iOS應用引入桌面,並根據需要通過額外的AppKit和平台API來增強它。
- 使用.NET MAUI構建的Windows應用使用WindowsUI庫(WinUI)3創建可以針對Windows桌面和通用視窗平台(UWP)的原生應用。
.NET MAUI單項目
.NET MAUI應用通常由一個可以針對安卓、iOS、macOS和Windows的項目組成。這提供了以下好處:
- 一個針對多個平台和設備的項目。
- 一個位置來管理資源,如字體和圖像。
- 多目標組織平台特定代碼。
支持平台
.NET多平台應用UI(MAUI)應用可用於以下平台編寫:
- Android 5.0 (API 21) or higher.
- iOS 10 or higher.
- macOS 11 (Big Sur) or higher.
- Windows desktop and the Universal Windows Platform (UWP), using Windows UI Library (WinUI) 3.
適用於安卓、iOS和視窗的NET MAUI應用程序可在視覺工作室內置。但是,使用最新版本的Xcode和Apple所需的macOS的最小版本進行iOS開發需要聯網Mac。
適用於安卓、iOS和macOS的NET茂宜島應用程序可在Mac視覺工作室內置。
前置條件
創建MAUI的前置條件是.Net的版本必須大於等於.NET 6 Preview 5
而且Visual Studio版本必須大於等於Visual Studio 16.11 Preview 2
如果要創建MAUI For Windows App,還需要額外安裝兩個擴展:
安裝.NET 6 Preview
我們可以使用官方提供的一個工具來檢查我們需要運行MAUI的環境是否齊全,還是很貼心哈。
安裝MAUI環境檢查工具(Maui.Check)
dotnet tool install -g Redth.Net.Maui.Check
運行MAUI環境檢查工具(Maui.Check)
maui-check
然后會彈出這個檢查工具的命令行界面,根據提示,一步步把缺失的進行安裝和補充即可。
如果發現打開maui-check工具后,立即閃退,據說改用管理員權限重新打開終端再執行能解決這個問題。
如果接下來,你繼續遇到了raw.githubusercontent.com:443
的報錯,要么全局代理,要么手動下載並且指定文件maui.manifest.json了。
maui-check -m maui.manifest.json
要命的是,如果你還是繼續遇到raw.githubusercontent.com:443
的報錯,估計是在maui.manifest.json
有個文件你過不去,最好也是手動下載並且寫死本地路徑最好。文件是:Versions.props
恭喜,這次算是過去了。
這里默認推薦你安裝OpenJDK 11
了,而且還是給你下載由微軟構建的Microsoft OpenJDK 11
發現少了安卓SDK,那就Fix吧!
發現少了.net 6全新的工作負載.NET SDK - Workloads,那就Fix吧!
發現少了MAUI SDK全家桶,各個平台的都要,那就Fix吧!
終於,可以愉快的結束了,謝謝你哦!
再執行一次,看看全貌!確認下勝利的果實。
體驗第一個MAUI項目
創建第一個MAUI項目“HelloMaui”
創建MAUI項目的方式有兩種,一種是基於Visual Studio 2019創建,一種是基於DotNet Core CLI創建。
1. 基於Visual Studio 2019創建。
2. 基於DotNet Core CLI創建。
dotnet new maui -n HelloMaui
基於DotNet-Cli
構建工具new
關鍵詞新建maui
項目模板的項目,項目名稱為HelloMaui
然后切換到項目目錄
cd HelloMaui
用Visual Studio Code打開它。
code .
還原初創建項目
dotnet restore
構建初創項目
dotnet build -t:Run -f net6.0-android
dotnet build -t:Run -f net6.0-ios
dotnet build -t:Run -f net6.0-maccatalyst
如果是IOS項目,還可以選擇模擬器:
dotnet build -t:Run -f net6.0-ios -p:_DeviceName=:v2:udid=<UDID>
運行第一個MAUI項目“HelloMaui”
折騰半天,發現用Visual Studio Code還是有點玩不裝MAUI,還是轉站Visual Studio 2019 Preview吧!
先用Visual Studio 2019 Preview打開Hello MaUI項目。
從結構上看,貌似是拆分成了WINUI項目和非WINUI項目,非WINUI項目主要是放Android、Linux、IOS平台的。
切換到Hello MAUI這個默認的選項后,運行,上來就是熟悉的,讓你創建安卓模擬器配置了。
根據提示,乖乖就范吧,不然怕出什么幺蛾子。
等它下載完畢后,我們啟動它,一般調試安卓都是要先開一個模擬器或者真機,這個流程我們還是知道的。
我的硬件還算爭氣,沒出幺蛾子,直接就運行成功模擬器了。
接下來,根據實操經驗,建議重啟一次Visual Studio,這樣它比較更好的識別我們的模擬器。
切換到Android Emulator模式,跑起來吧。
哈哈,不錯,算是開始順利,畢竟是成功部署進去了。
打量第一個MAUI項目“HelloMaui”
我們先順着官方指引,看看MAUI對包的依賴。
<ItemGroup>
<PackageReference Include="Microsoft.Maui" Version="6.0.100-preview.5.794" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ProjectReunion" Version="0.8.0-preview" />
<PackageReference Include="Microsoft.ProjectReunion.Foundation" Version="0.8.0-preview" />
<PackageReference Include="Microsoft.ProjectReunion.WinUI" Version="0.8.0-preview" />
<FrameworkReference Update="Microsoft.Windows.SDK.NET.Ref" RuntimeFrameworkVersion="10.0.19041.16" />
<FrameworkReference Update="Microsoft.Windows.SDK.NET.Ref" TargetingPackVersion="10.0.19041.16" />
</ItemGroup>
安卓模式下,能看到你的模擬器實例,到時候調試也是可以選擇的。
關於應用的資源設置,我們也可以找到線索,可以看到貌似為了保持多個平台一致性,官方的案例里面就直接用SVG這種矢量格式了,其實這還是蠻值得推薦的,就是設計師要注意輸出這種格式給開發了。
<ItemGroup>
<!-- App Icon -->
<MauiImage
Include="Resources\appicon.svg"
ForegroundFile="Resources\appiconfg.svg"
IsAppIcon="true"
Color="#512BD4" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\appiconfg.svg" Color="#512BD4" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
</ItemGroup>
針對不同平台的代碼是分了文件夾放置的,這點和React Native還是挺類似的。
啟動程序代碼還是比較按最新的.Net Core
的規范來,這里有個UseMauiApp
,應該是指定應用的根視圖的,加載字體的話,可以用ConfigureFonts
來定制。
public class Startup : IStartup
{
public void Configure(IAppHostBuilder appBuilder)
{
appBuilder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
}
}
既然啟動的時候,主動找了App
,那么我們就看下App內部做了什么,這里設置了下資源文件目錄,然后啟動了一個Microsoft.Maui.Controls.Window
類型的窗體。
public partial class App : Application
{
public App()
{
InitializeComponent();
}
protected override IWindow CreateWindow(IActivationState activationState)
{
this.On<Microsoft.Maui.Controls.PlatformConfiguration.Windows>()
.SetImageDirectory("Assets");
return new Microsoft.Maui.Controls.Window(new MainPage());
}
}
前往MainPage
,我們一看究竟,哇,這不就是對Windows開發童鞋最熟悉的Xaml,是不是很爽!這里只是控件全部基於Microsoft.Maui.Controls
重新來了一套嘛,其實很熟悉了。
using System;
using Microsoft.Maui.Controls;
namespace HelloMaui
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
int count = 0;
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
CounterLabel.Text = $"Current count: {count}";
}
}
}
我們再看看WINUI項目的內容,發現其實是個空殼子,所有的實現都不在這里,這里就是原來UWP那套應用配置項而已。
學習MAUI項目的基本玩法
1. 學習MAUI的啟動入口規范。
首先.NET Multi-platform App UI (MAUI)它是遵循了.NET 通用主機(.NET Generic Host)的啟動規范的,這個啟動入口的設計,就在主項目的Startup.cs
文件中。
在這個Startup.cs
中必須存在一個對IStartup
接口方法的實現,並且IAppHostBuilder必須至少添加一個應用的實現才可以跑起來,這里舉例就是appBuilder.UseMauiApp<App>()
這個。
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
public class Startup : IStartup
{
public void Configure(IAppHostBuilder appBuilder)
{
appBuilder.UseMauiApp<App>();
}
}
然后在App
中,又必須存在一個對Application
方法的繼承實現,並且重寫CreateWindow
方法,而且至少實現一個Window的創建返回。
using Microsoft.Maui;
using Microsoft.Maui.Controls;
public partial class App : Application
{
protected override IWindow CreateWindow(IActivationState activationState)
{
return new Window(new MainPage());
}
}
這里舉例的MainPage
窗體,是一個繼承自ContentPage
的實現者。
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
2. 在MAUI中玩轉自定義字體
如果需要注冊新的字體加到應用中,可以在Startup.cs
的Configure
方法中,通過ConfigureFonts
來添加。
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
public class Startup : IStartup
{
public void Configure(IAppHostBuilder appBuilder)
{
appBuilder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("Lobster-Regular.ttf", "Lobster");
});
}
}
在IFontCollection
對象的AddFont
方法中,第一個參數是字體的全稱路徑,第二個參數是這個字體的別名,並且所有定制字體都必須描述進項目的描述文件(.csproj
)中,這里可以直接用目錄 + *
來包括整個目錄的定制字體。
<ItemGroup>
<MauiFont Include="Resources\Fonts\*" />
</ItemGroup>
添加的字體,可以直接在Xaml里面,通過指定FontFamily
來使用,可以使用無格式后綴的文件名全稱,也可以用字體的別名。
<!-- Use font name -->
<Label Text="Hello .NET MAUI"
FontFamily="Lobster-Regular" />
<!-- Use font alias -->
<Label Text="Hello .NET MAUI"
FontFamily="Lobster" />
說了這么多,還是找個自定義字體試試,這里想到了自定義圖標字體,從https://icofont.com/icons 找了一組品牌的圖標字體,里面有微軟的圖標,下載后把ttf文件重命名下為Brand-IconFont.ttf
,然后拖到項目的\Resources\Fonts
文件夾下即可。
在Startup.cs
中,添加對這個字體的注冊,別名取為BrandIcoFont
吧。
public void Configure(IAppHostBuilder appBuilder)
{
appBuilder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("Brand-IconFont.ttf", "BrandIcoFont");
});
}
切換到構建界面的MainPage.xaml
,找個位置把使用字體的文本控件插進去。
<Label
Grid.Row="2"
FontFamily="BrandIcoFont"
FontSize="32"
HorizontalOptions="CenterAndExpand"
SemanticProperties.HeadingLevel="Level1"
Text="" />
這里我們用別名的方式指定FontFamily為BrandIcoFont
,至於Text的值,我們需要從字體網站獲取下,一般復制HTML Entity的值就好了。
好了,跑起來,看看效果吧,還行,基本達到預期,看到微軟Logo了。
3. 在MAUI中玩轉自定義事件。
如果需要將控件的自定義事件加到應用中,可以在Startup.cs
的Configure
方法中,通過ConfigureMauiHandlers
來添加。
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
public class Startup : IStartup
{
public void Configure(IAppHostBuilder appBuilder)
{
appBuilder
.UseMauiApp<App>()
.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(MyEntry), typeof(MyEntryHandler));
});
}
}
在IMauiHandlersCollection
對象的AddHandler
方法中,第一個參數是控件的實體名稱,第二個參數是需要綁定的自定義事件。
這樣注冊之后,所有MyEntry
控件就都會綁定MyEntryHandler
這個事件了。
4. 在MAUI中玩轉自定義渲染器。
如果需要將控件的自定義渲染器加到應用中,可以在Startup.cs
的Configure
方法中,通過ConfigureMauiHandlers
來添加。
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Controls.Compatibility;
public class Startup : IStartup
{
public void Configure(IAppHostBuilder appBuilder)
{
appBuilder
.UseMauiApp<App>()
#if __ANDROID__
.ConfigureMauiHandlers(handlers =>
{
handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.BoxView),
typeof(Microsoft.Maui.Controls.Compatibility.Platform.Android.BoxRenderer));
handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.Frame),
typeof(Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers.FrameRenderer));
});
#elif __IOS__
.ConfigureMauiHandlers(handlers =>
{
handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.BoxView),
typeof(Microsoft.Maui.Controls.Compatibility.Platform.iOS.BoxRenderer));
handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.Frame),
typeof(Microsoft.Maui.Controls.Compatibility.Platform.iOS.FrameRenderer));
});
#endif
}
}
這里可以通過跨平台設備的if條件,來編寫,通過IMauiHandlersCollection
對象的AddCompatibilityRenderer
方法來添加指定的控件走什么渲染器。
注意要添加using Microsoft.Maui.Controls.Compatibility;
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Controls.Compatibility;
namespace HelloMaui
{
public class Startup : IStartup
{
public void Configure(IAppHostBuilder appBuilder)
{
appBuilder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("Brand-IconFont.ttf", "BrandIcoFont");
})
#if __ANDROID__
.ConfigureMauiHandlers(handlers =>
{
handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.BoxView),
typeof(Microsoft.Maui.Controls.Compatibility.Platform.Android.BoxRenderer));
handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.Frame),
typeof(Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers.FrameRenderer));
});
#elif __IOS__
.ConfigureMauiHandlers(handlers =>
{
handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.BoxView),
typeof(Microsoft.Maui.Controls.Compatibility.Platform.iOS.BoxRenderer));
handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.Frame),
typeof(Microsoft.Maui.Controls.Compatibility.Platform.iOS.FrameRenderer));
});
#endif
}
}
}
5. 在MAUI中玩轉自定義控件。
.NET多平台應用UI(MAUI)提供可用於顯示數據、啟動操作、指示活動、顯示集合、拾取數據等的控件集合。默認情況下,處理程序將這些跨平台控件映射到每個平台上的原生控件。例如,在iOS上,Button
控件將會映射成UIButton
。在安卓系統上,Button
控件將會映射成AppCompatButton
。
比如我們要自定義安卓平台,所有的界面背景顏色。
using Microsoft.Maui;
using Microsoft.Maui.Controls;
public partial class App : Application
{
public App()
{
InitializeComponent();
#if __ANDROID__
Microsoft.Maui.Handlers.ViewHandler.ViewMapper[nameof(IView.BackgroundColor)] = (h, v) =>
{
(h.NativeView as Android.Views.View).SetBackgroundColor(Microsoft.Maui.Graphics.Colors.Cyan.ToNative());
};
#endif
}
}
如果要移除安卓組件的下划線
using Microsoft.Maui;
using Microsoft.Maui.Controls;
public partial class MainPage : ContentPage, IPage
{
public MainPage()
{
InitializeComponent();
#if __ANDROID__
Handlers.EntryHandler.EntryMapper[nameof(IEntry.BackgroundColor)] = (h, v) =>
{
(h.NativeView as global::Android.Views.Entry).UnderlineVisible = false;
};
#endif
}
}
如果想自定義控件,可以繼承自Entry來做。
using Microsoft.Maui.Controls;
namespace HelloMaui
{
public class MyEntry : Entry
{
}
}
還可以自定義一個處理給到自定義控件。
using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
namespace MauiApp1
{
public partial class App : Application
{
public App()
{
InitializeComponent();
Microsoft.Maui.Handlers.EntryHandler.EntryMapper[nameof(IView.BackgroundColor)] = (handler, view) =>
{
if (view is MyEntry)
{
#if __ANDROID__
handler.NativeView.SetBackgroundColor(Colors.Red.ToNative());
#elif __IOS__
handler.NativeView.BackgroundColor = Colors.Red.ToNative();
handler.NativeView.BorderStyle = UIKit.UITextBorderStyle.Line;
#elif WINDOWS
handler.NativeView.Background = Colors.Red.ToNative();
#endif
}
};
}
}
}
參考
- What is .NET MAUI?
- .NET MAUI single project
- .NET MAUI supported platforms
- .NET MAUI installation
- Build your first .NET MAUI app
- .NET MAUI app startup
- .NET MAUI single project
- Customize .NET MAUI controls with handlers
- Announcing .NET MAUI Preview 4
- .NET 6 deep dive; what's new and what's coming
- https://github.com/dotnet/maui
- https://github.com/dotnet/net6-mobile-samples
- Announcing .NET Multi-platform App UI Preview 3
- Getting Started
- https://github.com/redth/dotnet-maui-check
- Introducing .NET Multi-platform App UI
- The New .NET Multi-platform App UI
- https://themesof.net
- What is the difference between MAUI and WinUI?
- https://github.com/redth/dotnet-maui-check
- .Net MAUI檢查命令
maui-check
閃退的問題和解決方案 - .NET 6 亮點之工作負載,它是統一 .NET 的基礎
- 如何評價 .NET 官方跨平台 UI 框架 MAUI?
- .NET 6 RC1 正式發布
- Update on .NET Multi-platform App UI (.NET MAUI)