MAUI与Blazor共享一套UI,媲美Flutter,实现Windows、macOS、Android、iOS、Web通用UI

1. 前言

距离上次发《MAUI初体验:爽》一文已经过去2个月了,本计划是下半年或者明年再研究MAUI的,现在计划提前啦,因为我觉得MAUI Blazor挺有意思的:在Android、iOS、macOS、Windows之间共享UI,一处UI增加或者修改,就能得到一致的UI体验。

看看这篇文章《Blazor Hybrid/MAUI 简介和实战》对MAUI Blazor的说明:

MAUI

.NET 多平台应用程序 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移动和桌面应用程序, 使用 .net MAUI,可以开发可在 Android、iOS、macOS 上运行的应用,Windows 以及从单个共享代码库运行的应用。

Blazor Hybrid 应用和 .NET MAUI

Blazor Hybrid 支持内置于 .NET 多平台应用 UI (.NET MAUI) 框架。 .NET MAUI 包含 BlazorWebView 控件,该控件运行将 Razor 组件呈现到嵌入式 Web View 中。 通过结合使用 .NET MAUI 和 Blazor,可以跨移动设备、桌面设备和 Web 重复使用一组 Web UI 组件。

今天就分享如何在Blazor Server、Blazor Wasm、MAUI Blazor之间共享UI的实验,这一步完成,后面开发应用时就方便多了(只针对UI修改)。

2. 先来体验下各端最终效果

Windows桌面、Blazor Server(在线)、Blazor Wasm(在线)、Android效果

1301.gif

iPad Air、iOS、macOS桌面效果

1309.png

MAUI各端未做发布文件体验(需要做相应平台的发布签名等操作),大家可以按下面介绍的方法创建项目编译体验一下。

iOS和macOS效果感谢青城同学提供的图片素材,站长mbp安装了最新的macOS,xCode也是最新的,可能因为预览版macOS原因,xCode无法打开,间接影响了maui编译?

macOS版本和xCode版本

1306.png

xCode为不可用状态

1312.jpg

VS编译出错,后面再解决

1313.jpg

用mbp的同学建议不要安装预览版操作系统,不要当勇士....

3. 新建项目

关于MAUI的环境搭建可参考这篇文章《在MAUI中使用Masa Blazor》,本文不再介绍环境搭建,直接使用VS 2022最新预览版项目模板创建项目。

3.1 创建Blazor Server项目:Dotnet9.Server

1302.png

3.2 创建Blazor WebAssembly项目:Dotnet9.Wasm

1303.png

3.3 创建MAUI Blazor项目:Dotnet9.MAUI

1304.png

3.4 查找共同点

在3个项目的上一层目录,打开PowerShell,输入tree /f查看详细的目录文件组织结构:

1305.gif

仔细查看三个模板项目文件结构,我们找出共同的文件查看:

文件夹 PATH 列表
卷序列号为 76F5-AF62
C:.
│  Dotnet9.sln
│
├─Dotnet9.MAUI
【1 这里省略数个文件】
│  │
│  ├─Data
│  │      WeatherForecast.cs
│  │      WeatherForecastService.cs
│  │
│  ├─Pages
│  │      Counter.razor
│  │      FetchData.razor
│  │      Index.razor
【2 这里省略数个文件】
│  │
│  ├─Shared
│  │      MainLayout.razor
│  │      MainLayout.razor.css
│  │      NavMenu.razor
│  │      NavMenu.razor.css
│  │      SurveyPrompt.razor
【3 这里省略数个文件】
│
├─Dotnet9.Server
│  │  App.razor
【4 这里省略数个文件】
│  │
│  ├─Data
│  │      WeatherForecast.cs
│  │      WeatherForecastService.cs
│  │
│  ├─Pages
│  │      Counter.razor
│  │      Error.cshtml
│  │      Error.cshtml.cs
│  │      FetchData.razor
│  │      Index.razor
│  │      _Host.cshtml
│  │      _Layout.cshtml
│  │
│  ├─Properties
│  │      launchSettings.json
│  │
│  ├─Shared
│  │      MainLayout.razor
│  │      MainLayout.razor.css
│  │      NavMenu.razor
│  │      NavMenu.razor.css
│  │      SurveyPrompt.razor
【5 这里省略数个文件】
│
└─Dotnet9.Wasm
【6 这里省略数个文件】
    │
    ├─Pages
    │      Counter.razor
    │      FetchData.razor
    │      Index.razor
    │
    ├─Properties
    │      launchSettings.json
    │
    ├─Shared
    │      MainLayout.razor
    │      MainLayout.razor.css
    │      NavMenu.razor
    │      NavMenu.razor.css
    │      SurveyPrompt.razor
【7 这里省略数个文件】

发现都有Data目录和Pages目录(其中Wasm项目没有Data目录,使用的示例类是直接写在FetchData.razor文件@code{}中的),那把这部分文件直接提取到类库中就可以了,那就做吧。

4. 提取UI到Razor类库

创建Razor类库:Dotnet9.WebApp

1307.png

下面开始UI的提取

1308.png

如上图,将Dotnet9.MAUI项目的DataPagesShared三个目录外加Main.razor文件剪切到Dotnet9.WebApp项目中,然后修改剪切后相应文件的命名空间Dotnet9.MAUI[xxx]Dotnet9.WebApp[xxx],打开Dotnet9.WebApp项目的_Import.razor文件,参考Dotnet9.MAUI项目的_Import.razor文件部分命名空间,修改如下:

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Dotnet9.WebApp
@using Dotnet9.WebApp.Shared

上面部分命名空间可以删除(未尝试),编译Dotnet9.WebApp项目,检查是否正确编译。

5. 各端项目修改

5.1 MAUI项目

  1. 添加Dotnet9.WebApp项目引用
  2. Program.csusing Dotnet9.MAUI.Data;改为using Dotnet9.WebApp.Data
  3. 删除DataPagesShared三个目录外加Main.razor文件,上一步是剪切的话这步省略
  4. 修改_Imports.razor文件,主要是添加Dotnet9.WebApp项目命名空间引用
@using System.Net.Http
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Dotnet9.MAUI
@using Dotnet9.WebApp
@using Dotnet9.WebApp.Shared
  1. MauiProgram.cs修改引用的命名空间:using Dotnet9.MAUI.Data; => using Dotnet9.WebApp.Data;
  2. 打开MainPage.xaml,对路由组件命名空间的引用修改

添加命名空间xmlns:webApp="clr-namespace:Dotnet9.WebApp;assembly=Dotnet9.WebApp",修改代码如下:

修改前:

<RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />

修改后:

<RootComponent Selector="#app" ComponentType="{x:Type webApp:Main}" />

修改完毕,编译运行Dotnet9.MAUI项目吧,接下来修改Dotnet9.Server项目。

5.2 Blazor Server项目

  1. 添加Dotnet9.WebApp项目引用
  2. Program.csusing Dotnet9.Server.Data;改为using Dotnet9.WebApp.Data;
  3. 删除Data目录
  4. 删除Pages目录中的Counter.razorFetchData.razorIndex.razor三个文件(包括同名的.cs.css文件)
  5. 删除Shared目录
  6. 修改_Imports.razor文件,主要是添加Dotnet9.WebApp项目命名空间引用
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Dotnet9.Server
@using Dotnet9.WebApp
@using Dotnet9.WebApp.Shared
  1. 打开./Pages/_Host.cshtml文件,添加命名空间引用@using Dotnet9.WebApp,修改代码如下:

修改前:

<component type="typeof(App)" render-mode="ServerPrerendered" />

修改后:

<component type="typeof(Main)" render-mode="ServerPrerendered" />

修改完毕,编译运行Dotnet9.Server项目吧,接下来修改Dotnet9.Wasm项目。

5.3 Blazor Wasm项目

  1. 添加Dotnet9.WebApp项目引用
  2. 删除PagesShared目录外加App.razor文件
  3. Program.csusing Dotnet9.Wasm;改为using Dotnet9.WebApp;,同时修改代码

修改前

builder.RootComponents.Add<App>("#app");

修改后

builder.RootComponents.Add<Main>("#app");
  1. 修改_Imports.razor文件,主要是添加Dotnet9.WebApp项目命名空间引用
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Dotnet9.Server
@using Dotnet9.WebApp
@using Dotnet9.WebApp.Shared

修改完毕,编译运行Dotnet9.Wasm项目,至此三种项目模板已经修改完成,最终解决方案如下图:

1311.png

6 总结

总结就是下图:

1310.png
  • Dotnet9.WebApp:blazor组件相关的代码、路由组件等放在这个工程,供其他项目引用
  • Dotnet9.Server:Blazor Server模板项目
  • Dotnet9.Wasm:Blazor WebAssembly项目
  • Dotnet9.MAUI:MAUI Blazor项目

一句话:将UI封装到Razor类库Dotnet9.WebApp,其他终端工程(Dotnet9.ServerDotnet9.MAUIDotnet9.Wasm)引用此工程即可实现UI共享。

参考

  1. ASP.NET Community Standup - Native client apps with Blazor Hybrid
  2. Blazor一份代码在Blazor WebAssembly和Blazor Server之间任意切换
  3. 微软MAUI文档
  4. 微软Blazor文档
  5. 学Blazor
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容