MAUI笔记

.NET MAUI 基础知识总结(含路由跳转 + 弹窗详解)

PDA系统(登录、增删改查、路由跳转、下拉选择),

案例链接:https://download.csdn.net/download/ly1h1/92846901?spm=1001.2014.3001.5503


一、什么是 .NET MAUI?

.NET MAUI(.NET Multi-platform App UI) 是微软推出的跨平台移动 / 桌面 UI 框架,核心优势是「一套代码,多端运行」,无需为不同平台(Android、iOS、Windows、macOS)单独开发,极大降低跨平台开发成本。

核心特点:

  1. 开发语言:C# + XAML(界面描述语言),上手门槛低,适合.NET 开发者快速转型移动端
  2. 性能:接近原生应用,避免跨平台框架常见的性能损耗
  3. 集成能力:内置 MVVM、路由导航、网络请求、布局控件等全套开发能力,无需额外引入第三方库
  4. 适用场景:移动应用(手机端)、平板应用,也可扩展到桌面应用

二、MAUI 项目核心结构(实战对应版)

结合我们开发的智慧农业项目,标准 MAUI 项目结构如下(新手重点记这 5 个核心目录 / 文件):

plaintext

复制代码
agricultureMobile(项目名)
├── Views           // 页面目录(所有界面都在这里)
│   ├── LoginView.xaml      // 登录页(项目入口)
│   ├── FarmView.xaml       // 新增数据页
│   ├── ShedView.xaml       // 数据列表页(改/删功能)
│   └── EditView.xaml       // 编辑数据页
├── ViewModels      // 业务逻辑目录(MVVM核心)
│   ├── LoginViewModel.cs   // 登录逻辑
│   ├── FarmViewModel.cs    // 新增逻辑(下拉选择)
│   ├── ShedViewModel.cs    // 列表+修改+删除逻辑
│   └── EditViewModel.cs    // 编辑逻辑
├── Models          // 数据模型目录
│   └── DataItem.cs         // 数据实体(A1Real、A2String、A3String)
├── AppShell.xaml   // 路由导航配置(页面跳转核心)
└── MauiProgram.cs  // 应用服务注册(程序启动入口)

关键说明:

  • Views:只写界面(XAML),不写任何业务逻辑,保证「界面与逻辑分离」
  • ViewModels:所有业务逻辑(网络请求、数据处理、按钮点击事件)都在这里
  • AppShell.xaml:管理所有页面的路由,控制页面跳转规则,是 MAUI 导航的核心

三、XAML 基础(MAUI 界面开发语言)

XAML 是 MAUI 的界面描述语言,类似 HTML,语法简洁,专注于「描述界面布局和控件」,无需写代码就能完成界面开发。

1. 常用布局控件(实战高频)

布局控件用于控制页面元素的排列方式,我们项目中全部用到,新手重点掌握前 3 个:

表格

控件名称 作用 项目实战应用场景
<Grid /> 网格布局(最常用、最稳定) 实现列表滚动、按钮并排
<VerticalStackLayout /> 垂直排列(从上到下) 页面整体垂直布局、列表项内部布局
<HorizontalStackLayout /> 水平排列(从左到右) 按钮并排(修改 + 删除)
<ScrollView /> 滚动视图 新增页、登录页(避免内容溢出)
<CollectionView /> 列表控件(展示多条数据) 大棚数据列表展示

实战示例(Grid 布局,解决列表不滚动问题):

xml

复制代码
<!-- 根布局用Grid,实现列表正常滚动 -->
<Grid RowDefinitions="Auto,*">
    <!-- 第1行:刷新按钮(自适应高度) -->
    <Button Grid.Row="0" Text="刷新数据" Command="{Binding LoadCommand}" />
    <!-- 第2行:列表(占满剩余所有空间,支持滚动) -->
    <CollectionView Grid.Row="1" ItemsSource="{Binding Items}" />
</Grid>

2. 常用输入 / 展示控件(实战高频)

表格

控件名称 作用 项目实战应用场景
<Label /> 文本展示 显示 "数值:XXX""参数 2:XXX"
<Entry /> 文本输入框 输入用户名、密码、A1Real、A2String
<Picker /> 下拉选择框 A3String 下拉选择 A/B/C
<Button /> 按钮(触发事件) 登录、提交、修改、删除、刷新
<Border /> 圆角边框(美化界面) 图片、表单美化
<Image /> 图片展示 项目 Logo、大棚图片

实战示例(Picker 下拉选择框):

xml

复制代码
<!-- 下拉选择框,绑定ViewModel中的数据源 -->
<Picker BackgroundColor="#F5F5F5"
        Title="请选择参数3"
        ItemsSource="{Binding A3Options}"
        SelectedItem="{Binding A3String}"/>

四、MVVM 模式(MAUI 标准开发方式)

MVVM 是 MAUI 开发的核心模式,核心思想是「界面(View)与业务逻辑(ViewModel)分离」,便于维护和扩展,我们的项目全程采用这种模式。

1. MVVM 三要素(对应项目实战)

表格

要素 作用 项目对应文件
View 界面(XAML),只负责展示 LoginView.xaml、ShedView.xaml 等
ViewModel 业务逻辑,处理数据、网络请求、事件 LoginViewModel.cs、ShedViewModel.cs 等
Model 数据模型,存储数据结构 DataItem.cs(A1Real、A2String 等)

2. 核心接口:INotifyPropertyChanged(必写)

作用:实现 "数据变化 → 界面自动更新",比如输入框输入内容、下拉选择后,界面同步显示最新数据,是 MVVM 绑定的核心。

固定模板(所有 ViewModel 都要继承并实现,直接复制使用):

csharp

运行

复制代码
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace agricultureMobile.ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        // 事件:数据变化时触发
        public event PropertyChangedEventHandler? PropertyChanged;

        // 触发事件的方法(固定写法)
        protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

实战示例(ViewModel 中使用):

csharp

运行

复制代码
// 继承BaseViewModel,无需重复写INotifyPropertyChanged
public class FarmViewModel : BaseViewModel
{
    // 私有字段
    private string _a2String = string.Empty;

    // 公共属性(绑定到界面)
    public string A2String
    {
        get => _a2String;
        set 
        { 
            _a2String = value; 
            OnPropertyChanged(); // 数据变化时,通知界面更新
        }
    }
}

五、数据绑定 Binding(MAUI 灵魂)

数据绑定是连接 View 和 ViewModel 的桥梁,实现「界面与数据的自动关联」,无需手动操作界面控件,减少代码冗余。

1. 核心语法(实战常用)

xml

复制代码
<!-- 界面控件绑定 ViewModel 中的属性 -->
<Entry Text="{Binding A1Real}" /> <!-- 输入框绑定 A1Real 属性 -->
<Picker ItemsSource="{Binding A3Options}" SelectedItem="{Binding A3String}" /> <!-- 下拉框绑定数据源和选中值 -->
<Button Command="{Binding LoginCommand}" /> <!-- 按钮绑定 Command 事件 -->

2. 绑定关键要求

  1. View 的 BindingContext 必须设置为对应的 ViewModel(在 XAML 中设置): xml

    复制代码
    <ContentPage.BindingContext>
        <vm:LoginViewModel /> <!-- 绑定LoginViewModel -->
    </ContentPage.BindingContext>
  2. ViewModel 中的属性必须有 get/set,且在 set 中调用 OnPropertyChanged()

  3. 绑定的属性名必须和 ViewModel 中的属性名完全一致(大小写敏感)


六、命令 Command(按钮点击事件,MVVM 模式)

MAUI 中,按钮点击不推荐用传统的点击事件(如 Click),而是用「Command 命令」,完全符合 MVVM 模式,实现 "界面与逻辑分离"。

1. 实战写法(固定模板)

第一步:ViewModel 中定义 Command

csharp

运行

复制代码
using System.Windows.Input;
using Microsoft.Maui.Controls;

public class LoginViewModel : BaseViewModel
{
    // 定义Command
    public ICommand LoginCommand { get; }

    // 构造函数中绑定Command对应的方法
    public LoginViewModel()
    {
        // 绑定LoginAsync方法(按钮点击后执行)
        LoginCommand = new Command(async () => await LoginAsync());
    }

    // Command对应的业务逻辑方法
    private async Task LoginAsync()
    {
        // 登录逻辑(网络请求、参数校验等)
    }
}

第二步:XAML 中绑定 Command

xml

复制代码
<Button Text="登录" 
        Command="{Binding LoginCommand}"
        BackgroundColor="#0FC97D"
        TextColor="White"/>

2. 带参数的 Command(修改 / 删除功能必备)

比如列表中的 "修改""删除" 按钮,需要传递当前选中的数据,写法如下:

csharp

运行

复制代码
// 带参数的Command(参数类型为DataItem)
public ICommand DeleteCommand { get; }

public ShedViewModel()
{
    // 绑定带参数的DeleteItem方法
    DeleteCommand = new Command<DataItem>(DeleteItem);
}

// 接收参数(当前选中的列表项)
private async void DeleteItem(DataItem item)
{
    // 根据item.A1Real执行删除逻辑
}

XAML 绑定(传递当前列表项作为参数):

xml

复制代码
<Button Text="删除"
        Command="{Binding Source={x:Reference thisPage}, Path=BindingContext.DeleteCommand}"
        CommandParameter="{Binding}" />

七、MAUI Shell 路由(页面跳转,核心重点)

Shell 是 MAUI 的路由导航系统,负责管理所有页面的跳转、传参、返回,我们项目中解决的 "路由报错、无法返回" 问题,都和 Shell 路由相关。

1. 路由注册(必须在 AppShell.xaml 中配置)

所有需要跳转的页面,都要在 AppShell.xaml 中注册路由,否则无法跳转。

实战示例(我们项目的路由配置):

xml

复制代码
<Shell
    x:Class="agricultureMobile.AppShell"
    xmlns:v="clr-namespace:agricultureMobile.Views"
    Shell.FlyoutBehavior="Disabled">

    <!-- 1. 登录页(启动页,不放在TabBar中) -->
    <ShellContent Route="LoginView" ContentTemplate="{DataTemplate v:LoginView}" />

    <!-- 2. 底部Tab页(主页面) -->
    <TabBar>
        <ShellContent Title="农场2" ContentTemplate="{DataTemplate v:FarmView}" Icon="nc_icon.png" />
        <ShellContent Title="大棚" ContentTemplate="{DataTemplate v:ShedView}" Icon="dp_icon.png" Route="ShedView" />
        <ShellContent Title="我的" ContentTemplate="{DataTemplate v:MineView}" Icon="wd_icon.png" />
    </TabBar>

    <!-- 3. 编辑页(子页面,不放在TabBar中) -->
    <ShellContent ContentTemplate="{DataTemplate v:EditView}" Route="EditView"/>

</Shell>

关键规则:

  • 主页面(底部 Tab)放在 <TabBar>
  • 子页面(编辑页、弹窗页)放在 <TabBar> 外部,避免无法返回
  • 每个页面必须指定 Route="页面名"(如 Route="ShedView"),用于跳转时定位页面

2. 两种核心跳转方式(重点!含弹窗)

MAUI Shell 跳转分为「普通页面跳转」和「模态弹窗跳转」,我们项目中用的是普通跳转,弹窗跳转可按需扩展。

方式 1:普通页面跳转(非弹窗,最常用)

语法:///页面路由名(三个斜杠,绝对路由,避免报错)

csharp

运行

复制代码
// 登录成功后,跳转到大棚列表页(普通跳转,非弹窗)
await Shell.Current.GoToAsync("///ShedView");

// 列表页跳转到编辑页(带参数传参)
await Shell.Current.GoToAsync($"///EditView?A1Real={item.A1Real}&A2String={item.A2String}");
特点:
  • 页面压入导航栈,支持返回(用 .. 返回上一页)
  • 无弹窗效果,是正常的页面切换
  • 适合:登录→主页、列表→编辑等场景

方式 2:模态弹窗跳转(弹窗效果)

语法:modal://页面路由名(加 modal://,弹窗形式打开)

csharp

运行

复制代码
// 以弹窗形式打开编辑页
await Shell.Current.GoToAsync("modal://EditView");
特点:
  • 从页面底部弹出,类似手机弹窗效果
  • 弹窗期间,无法操作底层页面
  • 关闭弹窗(返回):await Shell.Current.GoToAsync("..");
  • 适合:新增、编辑等需要临时打开的页面

3. 路由传参与接收(修改功能必备)

第一步:跳转时传参(列表→编辑页)

csharp

运行

复制代码
// 传递A1Real、A2String、A3String三个参数
await Shell.Current.GoToAsync($"///EditView?A1Real={item.A1Real}&A2String={item.A2String}&A3String={item.A3String}");

第二步:编辑页接收参数(ViewModel 中)

[QueryProperty] 特性接收参数,自动绑定到对应的属性:

csharp

运行

复制代码
using Microsoft.Maui.Controls;

[QueryProperty(nameof(A1Real), nameof(A1Real))] // 接收A1Real参数
[QueryProperty(nameof(A2String), nameof(A2String))] // 接收A2String参数
[QueryProperty(nameof(A3String), nameof(A3String))] // 接收A3String参数
public class EditViewModel : BaseViewModel
{
    private double _a1Real;
    private string _a2String = string.Empty;
    private string _a3String = string.Empty;

    // 接收的参数会自动赋值到这些属性
    public double A1Real
    {
        get => _a1Real;
        set { _a1Real = value; OnPropertyChanged(); }
    }

    public string A2String
    {
        get => _a2String;
        set { _a2String = value; OnPropertyChanged(); }
    }

    public string A3String
    {
        get => _a3String;
        set { _a3String = value; OnPropertyChanged(); }
    }
}

4. 页面返回(解决 "无法返回" 问题)

表格

场景 返回写法 实战应用
普通跳转返回上一页 await Shell.Current.GoToAsync(".."); 编辑页返回列表页
强制返回主页面 await Shell.Current.GoToAsync("///ShedView"); 任意页面返回大棚列表页
弹窗关闭 await Shell.Current.GoToAsync(".."); 弹窗编辑页关闭返回列表页

八、MAUI 网络请求(HttpClient,实战必备)

我们项目中的登录、新增、修改、删除、列表加载,都用到了 HttpClient 进行网络请求,这是 MAUI 中最常用的网络请求方式。

1. 核心写法(固定模板)

第一步:初始化 HttpClient(ViewModel 中)

csharp

运行

复制代码
using System.Net.Http;

public class LoginViewModel : BaseViewModel
{
    // 初始化HttpClient(全局一个即可)
    private readonly HttpClient _httpClient = new HttpClient();

    // 后续所有网络请求都用这个_httpClient
}

第二步:不同请求方式(对应项目接口)

表格

接口类型 写法示例(实战对应)
POST(登录 / 新增) 登录:await _httpClient.PostAsync("http://193.112.175.157:8001/login", content);
PUT(修改) 修改:await _httpClient.PutAsync($"http://193.112.175.157:8001/update/{_originalA1Real}", content);
DELETE(删除) 删除:await _httpClient.DeleteAsync($"http://193.112.175.157:8001/delete/{item.A1Real}");
GET(列表加载) 列表:var res = await _httpClient.GetStringAsync("http://193.112.175.157:8001/list");

第三步:请求参数构造(JSON 格式)

csharp

运行

复制代码
using Newtonsoft.Json;
using System.Text;

// 1. 构造请求参数(和接口入参一致)
var requestObj = new
{
    username = Username,
    password = Password
};

// 2. 转换为JSON字符串
var json = JsonConvert.SerializeObject(requestObj);

// 3. 构造请求内容(指定编码和格式)
var content = new StringContent(json, Encoding.UTF8, "application/json");

九、MAUI 弹窗提示(用户交互必备)

用于显示提示信息(成功、失败、确认),我们项目中登录、新增、修改、删除都用到了,写法固定。

1. 常用弹窗类型

(1)提示弹窗(只有确定按钮)

csharp

运行

复制代码
// 格式:await Shell.Current.CurrentPage.DisplayAlert("标题", "内容", "确定按钮文本");
await Shell.Current.CurrentPage.DisplayAlert("成功", "删除完成", "确定");

(2)确认弹窗(确定 + 取消按钮,用于删除、危险操作)

csharp

运行

复制代码
// 格式:await Shell.Current.CurrentPage.DisplayAlert("标题", "内容", "确定按钮", "取消按钮");
bool confirm = await Shell.Current.CurrentPage.DisplayAlert("确认删除", $"确定要删除 {item.A1Real} 吗?", "确定", "取消");
if (confirm)
{
    // 用户点击确定,执行删除逻辑
}
else
{
    // 用户点击取消,取消操作
}

一、传统弹窗(.NET MAUI 通用写法)

1. 打开弹窗

csharp

运行

复制代码
var editPage = new EditView();
await Navigation.PushModalAsync(editPage);

csharp

运行

复制代码
await Shell.Current.Navigation.PushModalAsync(new EditView());

2. 关闭弹窗

csharp

运行

复制代码
await Navigation.PopModalAsync();

十、MAUI 实战避坑指南(新手必看)

结合我们项目中遇到的问题,整理 10 个高频坑,避免新手走弯路:

  1. 列表(CollectionView)不滚动、显示不全?→ 不要放在 VerticalStackLayout 中,用 Grid 布局(RowDefinitions="Auto,*")
  2. 路由报错 "Relative routing not supported"?→ 跳转用绝对路由(/// 页面名),不要用相对路由
  3. 编辑页无法返回?→ 编辑页(EditView)不要放在 TabBar 中,放在 TabBar 外部
  4. 按钮提示 "FillAndExpand 已过时"?→ 用 Grid 替代 HorizontalStackLayout 实现按钮并排,避免用 FillAndExpand
  5. 数据绑定后,界面不更新?→ 忘记在 ViewModel 的属性 set 中调用 OnPropertyChanged ()
  6. 路由传参接收不到?→ 确保 QueryProperty 的属性名和传参名一致,且属性有 get/set
  7. 弹窗提示报错 "MainPage 为空"?→ 用 Shell.Current.CurrentPage.DisplayAlert (),不要用 Application.Current.MainPage
  8. 下拉选择框(Picker)无数据?→ 确保 ItemsSource 绑定的集合已初始化(如 ObservableCollection)
  9. 网络请求失败?→ 检查接口地址是否正确,参数格式是否为 JSON
  10. 页面跳转后,数据不刷新?→ 跳转后手动调用列表加载方法(如 LoadData ())

十一、MAUI 学习路线

结合我们的实战项目,新手可以按以下顺序学习,快速上手 MAUI:

  1. 基础:XAML 布局(Grid、StackLayout、CollectionView 等常用控件)
  2. 核心:MVVM 模式 + INotifyPropertyChanged + 数据绑定
  3. 进阶:Shell 路由(跳转、传参、返回、弹窗)
  4. 实战:网络请求(HttpClient + JSON 解析)
  5. 扩展:表单输入(Entry、Picker)、弹窗提示、列表增删改查
  6. 优化:界面美化、性能优化、避坑技巧
相关推荐
Hello_Embed1 小时前
Windows 安装 Claude Code 并接入 模型
windows·笔记·ai编程
大Mod_abfun2 小时前
数字媒体艺术概论(课堂作业/笔记)
笔记·媒体
笑鸿的学习笔记2 小时前
qt-C++语法笔记之Qt Graphics View 框架中的类型辨析完全指南
c++·笔记·qt
U盘失踪了2 小时前
调用大模型API上下文关联
笔记
sakiko_3 小时前
UIKit学习笔记3-布局、滚动视图、隐藏或显示视图
前端·笔记·学习·objective-c·swift·uikit
koo3643 小时前
周报5.3
笔记
xian_wwq4 小时前
【学习笔记】Harness到底是什么
笔记·学习·ai·harness
二哈赛车手4 小时前
新人笔记---项目中简易版的RAG检索后评测指标(@Recall ,Mrr..)实现
java·开发语言·笔记·spring·ai
是上好佳佳佳呀5 小时前
【前端(十二)】JavaScript 函数与对象笔记
前端·javascript·笔记