MAUI正式版发布半年了,Net 7也发布了,再次学习MAUI跨平台开发。UI类型选择Blazor,因为Html的生态圈比Xaml好太多了,能用Html解决的,就不要用Xaml。Blazor既可以开发网页客户端,又可以开发移动客户端,一个技术栈可以涵盖前后端,多平台,做小型工具软件不错。
开发环境Win10,Visual Studio 17.5.0 Preview 1.0,安卓模拟器Pixle 5 - API 30(Android 11.0 - API 30)。
新建项目
新建MAUI Blazor项目,选择位置D:\Software\gitee\mauiblazorapp。
选择Net 7.0框架。目标平台仅保留安卓和Windows。
D:\Software\gitee\mauiblazorapp\MaBlaApp\MaBlaApp.csproj
<TargetFrameworks>net7.0-android</TargetFrameworks>
<!--<TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks>-->
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
Shell布局
MAUI Blazor项目是网站布局,但是手机APP最常见的布局还是微信这种,Xamarin Forms项目可以直接选择Shell模板,MAUI Xaml项目也可以,但是MAUI Blazor项目不行,这真是一大遗憾。
幸好微软提供了一个移动APP布局的DEMO,这个项目是专门为了展示MAUI跨平台开发特性而设计的,有XAML和Blazor两个版本。
https://github.com/microsoft/dotnet-podcasts
打开其中的MAUI Blazor项目。
D:\Software\Test\dotnet-podcasts-main\src\MobileBlazor\mauiapp\NetPodsMauiBlazor.csproj
参照着DEMO的MainLayout.razor和css文件修改。对于不熟悉Html和css的.Net开发者而言,也是非常头大的工作。希望MAUI Blazor也能提供Shell布局的模板。
D:\Software\Test\dotnet-podcasts-main\src\Web\Pages\Shared\MainLayout.razor
D:\Software\Test\dotnet-podcasts-main\src\Web\Pages\Shared\MainLayout.razor.css
官网DEMO采用grid布局实现了Shell布局效果。这个其实可以参考Xaml的Grid布局或者StackLayout布局去理解,把一个窗口横着分几块,每一块里边再竖着分几块。手机小尺寸页面的情况下,横向分4块。PC大尺寸页面的情况下,横向分3块。
.page {
background-color: var(--c-neutral-grey1);
display: grid;
grid-template-areas:
"header"
"main-view"
"player-bar"
"nav-bar";
grid-template-columns: 1fr;
grid-template-rows: 56px 1fr auto auto;
position: relative;
height: 100%;
width: 100%;
}
@media screen and (min-width: 992px) {
.page {
grid-template-areas:
"header main-view"
"nav-bar main-view"
"player-bar player-bar";
grid-template-columns: auto 1fr;
grid-template-rows: 142px 1fr auto;
}
我写的DEMO很简单,手机小尺寸页面的情况下,横向分2块。PC大尺寸页面的情况下,横向分1块,相比dotnet-podcasts项目没有header和player-bar播放栏。简而言之,就只有nav-bar导航栏和main-view主视图,小尺寸页面上下排列,大尺寸页面左右排列。
D:\Software\gitee\mauiblazorapp\MaBlaApp\Shared\MainLayout.razor.css
.page {
background-color: var(--c-neutral-grey1);
display: grid;
grid-template-areas:
"main-view"
"nav-bar";
grid-template-columns: 1fr;
grid-template-rows: 1fr auto;
position: relative;
height: 100%;
width: 100%;
}
@media screen and (min-width: 992px) {
.page {
grid-template-areas:
"nav-bar main-view";
grid-template-columns: auto 1fr;
grid-template-rows: 1fr;
}
然后再借鉴dotnet-podcasts项目的菜单样式。
D:\Software\Test\dotnet-podcasts-main\src\Web\Pages\Shared\NavMenu.razor
D:\Software\Test\dotnet-podcasts-main\src\Web\Pages\Shared\NavMenu.razor.css
.navbarApp__wrapper {
display: grid;
grid-template-columns: repeat(5, 1fr);
}
我写的DEMO减少为3个按钮
.navbarApp__wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
3个按钮就是Blazor项目模板自带的
D:\Software\gitee\mauiblazorapp\MaBlaApp\Shared\NavMenu.razor
<div class="navbarApp">
<nav class="navbarApp__wrapper">
<div class="navbarApp-item">
<NavLink class="navbarApp-link" href="" Match="NavLinkMatch.All">
<span class="navbarApp-linkIcon oi oi-home" aria-hidden="true"></span>
<span class="navbarApp-linkLabel">Home</span>
</NavLink>
</div>
<div class="navbarApp-item">
<NavLink class="navbarApp-link" href="counter">
<span class="navbarApp-linkIcon oi oi-plus" aria-hidden="true"></span>
<span class="navbarApp-linkLabel">Counter</span>
</NavLink>
</div>
<div class="navbarApp-item">
<NavLink class="navbarApp-link" href="fetchdata">
<span class="navbarApp-linkIcon oi oi-list-rich" aria-hidden="true"></span>
<span class="navbarApp-linkLabel">Fetch data</span>
</NavLink>
</div>
</nav>
</div>
@code {
}
把dotnet-podcasts项目的字体也搬过来
D:\Software\Test\dotnet-podcasts-main\src\Web\Components\wwwroot\css\fonts
放在wwwroot\css\fonts
D:\Software\gitee\mauiblazorapp\MaBlaApp\wwwroot\css\fonts
照搬dotnet-podcasts项目的字体、颜色定义
D:\Software\Test\dotnet-podcasts-main\src\Web\Components\wwwroot\css\styles.css
放在app.css里边
D:\Software\gitee\mauiblazorapp\MaBlaApp\wwwroot\css\app.css
@font-face {
font-display: swap;
font-family: "Segoe UI Light";
font-style: normal;
font-weight: 100;
src: url("fonts/SegoeUILight.woff") format("woff");
}
@font-face {
font-display: swap;
font-family: "Segoe UI Semilight";
font-style: normal;
font-weight: 300;
src: url("fonts/SegoeUISemilight.woff") format("woff");
}
@font-face {
font-display: swap;
font-family: "Segoe UI";
font-style: normal;
font-weight: 400;
src: url("fonts/SegoeUI.woff") format("woff");
}
@font-face {
font-display: swap;
font-family: "Segoe UI Semibold";
font-style: normal;
font-weight: 600;
src: url("fonts/SegoeUISemibold.woff") format("woff");
}
@font-face {
font-display: swap;
font-family: "Segoe UI Bold";
font-style: normal;
font-weight: 700;
src: url("fonts/SegoeUIBold.woff") format("woff");
}
body {
--c-primary: #c00cc0;
--c-primary-dark: #8b0995;
--c-primary-light: #dd0eb3;
--c-primary-xlight: #fcf2f7;
--c-primary-xxlight: #fcf2f4;
--c-secondary: #e10890;
--c-neutral-black: #000000;
--c-neutral-dark: #1f1f1f;
--c-neutral-grey1: #faf9f8;
--c-neutral-grey2: #f3f2f1;
--c-neutral-grey3: #edebe9;
--c-neutral-grey4: #d2d0ce;
--c-neutral-grey5: #c8c6c4;
--c-neutral-grey6: #a19f9d;
--c-neutral-grey7: #605e5c;
--c-neutral-grey8: #3b3a39;
--c-neutral-grey9: #323130;
--c-neutral-white: #ffffff;
--c-neutral-white-featured: #ffffff;
--c-neutral-white-complementary: #ffffff;
--c-player-range: #605e5c;
--c-waves-image: #f3f2f1;
--c-searchbar: transparent;
--c-switch: #605e5c;
--c-switch-dark: #1f1f1f;
--c-category: #f3f2f1;
--gradient-primary-color: #c00cc0;
--gradient-secondary-color: #e10890;
--gradient-final: radial-gradient( 101.22% 790.99% at 0% 3.41%, #c00cc0 0%, #e10890 100% );
--gradient-splash: linear-gradient(114.18deg, #bf26f5 0%, #e2068c 100%);
--ff-light: "Segoe UI Light";
--ff-semilight: "Segoe UI Semilight";
--ff-regular: "Segoe UI";
--ff-semibold: "Segoe UI Semibold";
--ff-bold: "Segoe UI Bold";
--headlines-h1-fs: 90px;
--headlines-h1-lh: 96px;
--headlines-h2-fs: 64px;
--headlines-h2-lh: 72px;
--headlines-h3-fs: 48px;
--headlines-h3-lh: 56px;
--headlines-h4-fs: 36px;
--headlines-h4-lh: 44px;
--headlines-h5-fs: 32px;
--headlines-h5-lh: 40px;
--headlines-h6-fs: 24px;
--headlines-h6-lh: 36px;
--subheadings-s1-fs: 36px;
--subheadings-s1-lh: 44px;
--subheadings-s2-fs: 32px;
--subheadings-s2-lh: 42px;
--subheadings-s3-fs: 24px;
--subheadings-s3-lh: 40px;
--subheadings-s4-fs: 22px;
--subheadings-s4-lh: 32px;
--subheadings-s5-fs: 20px;
--subheadings-s5-lh: 30px;
--subheadings-s6-fs: 18px;
--subheadings-s6-lh: 28px;
--text-xxl-fs: 24px;
--text-xxl-lh: 32px;
--text-xl-fs: 20px;
--text-xl-lh: 30px;
--text-l-fs: 18px;
--text-l-lh: 28px;
--text-m-fs: 16px;
--text-m-lh: 24px;
--text-s-fs: 14px;
--text-s-lh: 22px;
--text-xs-fs: 12px;
--text-xs-lh: 18px;
--link-xl-fs: 20px;
--link-xl-lh: 30px;
--link-l-fs: 18px;
--link-l-lh: 28px;
--link-m-fs: 16px;
--link-m-lh: 24px;
--link-s-fs: 14px;
--link-s-lh: 22px;
--input-fs: 15px;
--input-lh: 24px;
--box-shadow-s: 0 1px 4px rgba(0, 0, 0, 0.15), 0 1px 1px rgba(0, 0, 0, 0.05);
--box-shadow-m: 0 6px 12px rgba(0, 0, 0, 0.1), 0 1px 4px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(0, 0, 0, 0.05);
--box-shadow-l: 0 10px 24px rgba(0, 0, 0, 0.08), 0 12px 16px rgba(0, 0, 0, 0.08), 0 4px 8px rgba(0, 0, 0, 0.03);
--box-shadow-xl: 0 30px 48px rgba(0, 0, 0, 0.16), 0 8px 30px rgba(0, 0, 0, 0.14), 0 8px 10px rgba(0, 0, 0, 0.1), 0 4px 12px -10px rgba(0, 0, 0, 0.5);
}
html,
body,
#app,
.page {
height: 100%;
}
测试效果
在Windows平台跑一下,宽屏,竖屏效果都对了
DEMO代码地址:https://gitee.com/woodsun/mauiblazorapp