.NET MAUI 2024 开发指南:从入门到精通
引言:跨平台开发的新时代
在移动应用开发领域,开发者面临着如何高效构建同时满足 iOS、Android、Windows 和 macOS 多平台需求的挑战。传统方案要么需要为每个平台编写独立代码,要么使用跨平台框架但牺牲原生体验。.NET MAUI(Multi-platform App UI)作为微软推出的新一代跨平台框架,通过统一的 API 和原生渲染能力,为开发者提供了"一次编写,多平台运行"的解决方案。
本文将全面覆盖 .NET MAUI 的核心概念、开发流程、性能优化和实战技巧,帮助开发者快速掌握这一强大工具,构建高性能、跨平台的应用程序。
一、.NET MAUI 基础架构与核心优势
1.1 框架架构
.NET MAUI 采用分层架构设计,主要包含以下组件:
- UI 层:基于 XAML 和 C# 构建的声明式 UI 系统,提供丰富的控件库
- 平台抽象层:统一的 API 接口,屏蔽不同平台的实现差异
- 渲染层:将跨平台抽象转换为各平台原生控件
- 平台适配层:处理特定平台的功能和特性
1.2 核心优势
相比传统跨平台方案,.NET MAUI 具有以下显著优势:
- 单一代码库:使用 C# 和 .NET 编写一套代码,即可部署到多平台
- 原生性能:通过原生渲染引擎,提供接近原生应用的性能体验
- 丰富控件集:内置数百个跨平台控件,自动适配各平台设计规范
- 热重载支持:修改代码后无需重启应用,即时查看效果
- 与 .NET 生态无缝集成:可直接使用 Entity Framework、ASP.NET Core 等 .NET 生态组件
- 增量迁移:支持从 Xamarin.Forms 平滑迁移,降低升级成本
1.3 与其他框架对比
| 特性 | .NET MAUI | Flutter | React Native | Xamarin.Forms |
|---|---|---|---|---|
| 语言 | C# | Dart | JavaScript/TypeScript | C# |
| 渲染方式 | 原生控件 | 自绘 UI | 原生控件 | 原生控件 |
| 性能 | 接近原生 | 优秀 | 良好 | 良好 |
| 平台覆盖 | iOS/Android/Windows/macOS | iOS/Android/Windows/macOS | iOS/Android | iOS/Android/Windows/macOS |
| 开发效率 | 高(C# 开发者友好) | 中(需学习 Dart) | 中(需学习 React) | 中(Xamarin 生态) |
二、开发环境搭建
2.1 系统要求
- Windows:Windows 10 1909+ 或 Windows 11,Visual Studio 2022 17.4+
- macOS:macOS 12+,Visual Studio for Mac 17.4+
- Linux:Ubuntu 22.04+(可选,需安装 .NET SDK)
2.2 安装 Visual Studio 与工作负载
Windows 安装步骤:
- 下载 Visual Studio 2022:https://visualstudio.microsoft.com/zh-hans/downloads/
- 安装时选择 .NET Multi-platform App UI 开发 工作负载
- 安装完成后,在 Visual Studio 中通过 Tools > Get Tools and Features 确认工作负载已安装
macOS 安装步骤:
- 下载 Visual Studio for Mac:https://visualstudio.microsoft.com/vs/mac/
- 安装后,通过 Visual Studio > Settings > Workloads 选择 .NET MAUI 工作负载
2.3 配置模拟器与物理设备
Android 环境配置:
- 安装 Android SDK:通过 Visual Studio 扩展安装 "Android SDK Manager"
- 创建模拟器:在 Visual Studio 中打开 Device Manager,创建虚拟设备
- 启用 USB 调试:在 Android 设备开发者选项中开启 USB 调试
iOS 环境配置(仅 macOS):
- 安装 Xcode:从 Mac App Store 安装最新版 Xcode
- 配置签名证书:在 Apple Developer 网站注册并下载证书
- 启用开发者模式:在 Xcode > Preferences > Accounts 中添加 Apple ID
2.4 验证安装
创建并运行一个简单的 .NET MAUI 项目:
- 打开 Visual Studio,选择 Create a new project
- 搜索 .NET MAUI App 模板,点击创建
- 选择目标平台(Android/iOS/Windows),点击 Create
- 点击 Run 按钮,查看应用是否能在所选设备/模拟器中正常运行
三、项目结构与核心文件解析
3.1 项目结构
MyMauiApp/
├── App.xaml # 应用入口点,定义全局资源
├── App.xaml.cs # App.xaml 的代码隐藏文件
├── AppShell.xaml # 应用导航框架(Shell)
├── MainPage.xaml # 主页面,定义 UI 结构
├── MainPage.xaml.cs # MainPage.xaml 的代码隐藏文件
├── MauiProgram.cs # 应用启动配置
├── Platforms/ # 平台特定代码
│ ├── Android/ # Android 平台代码
│ ├── iOS/ # iOS 平台代码
│ ├── MacCatalyst/ # macOS 平台代码
│ └── Windows/ # Windows 平台代码
└── Resources/ # 应用资源
├── Images/ # 图像资源
├── Fonts/ # 字体资源
└── Styles/ # 样式资源
3.2 核心文件详解
MauiProgram.cs(应用入口)
using Microsoft.Maui;
using Microsoft.Maui.Controls;
namespace MyMauiApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
// 注册服务
builder.Services.AddSingleton<MainViewModel>();
return builder.Build();
}
}
AppShell.xaml(导航框架)
<Shell
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyMauiApp"
x:Class="MyMauiApp.AppShell">
<Shell.ContentTemplate>
<DataTemplate>
<NavigationPage>
<NavigationPage.TitleView>
<Label Text="My App" FontSize="Title" />
</NavigationPage.TitleView>
<Shell.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Label Text="{Binding Title}" />
</Grid>
</DataTemplate>
</Shell.ItemTemplate>
</NavigationPage>
</DataTemplate>
</Shell.ContentTemplate>
<TabBar>
<Tab Title="Home" Icon="home.png">
<ShellContent
ContentTemplate="{DataTemplate local:HomePage}"
Route="HomePage" />
</Tab>
<Tab Title="Profile" Icon="profile.png">
<ShellContent
ContentTemplate="{DataTemplate local:ProfilePage}"
Route="ProfilePage" />
</Tab>
</TabBar>
</Shell>
四、UI 开发基础
4.1 XAML 语法与控件
基本页面布局
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyMauiApp.MainPage"
Title="欢迎使用 .NET MAUI">
<VerticalStackLayout Padding="20" Spacing="10">
<!-- 标题标签 -->
<Label
Text="欢迎使用 .NET MAUI"
FontSize="24"
FontAttributes="Bold"
HorizontalOptions="Center" />
<!-- 输入框 -->
<Entry
Placeholder="请输入您的名字"
WidthRequest="250"
HorizontalOptions="Center" />
<!-- 按钮 -->
<Button
Text="点击我"
Clicked="OnButtonClicked"
BackgroundColor="#007AFF"
TextColor="White"
HorizontalOptions="Center"
WidthRequest="150" />
<!-- 结果显示 -->
<Label
x:Name="ResultLabel"
HorizontalOptions="Center"
FontSize="18" />
</VerticalStackLayout>
</ContentPage>
代码隐藏逻辑
using Microsoft.Maui.Controls;
namespace MyMauiApp;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void OnButtonClicked(object sender, EventArgs e)
{
var name = ResultLabel.Text;
ResultLabel.Text = $"Hello, {name}!";
}
}
4.2 布局管理
常用布局控件对比
| 布局类型 | 适用场景 | 特点 |
|---|---|---|
| VerticalStackLayout | 垂直排列控件 | 简单线性布局,性能优异 |
| HorizontalStackLayout | 水平排列控件 | 简单线性布局,性能优异 |
| Grid | 二维网格布局 | 适合行列复杂的界面 |
| AbsoluteLayout | 精确位置布局 | 适合需要精确定位的场景 |
| ScrollView | 滚动内容容器 | 处理超出屏幕的内容 |
Grid 布局示例
<Grid RowDefinitions="*, 1*, 2*"
ColumnDefinitions="100, *, Auto"
Padding="10"
Spacing="5">
<Label Grid.Row="0" Grid.Column="0" Text="Row 0, Col 0" />
<Label Grid.Row="0" Grid.Column="1" Text="Row 0, Col 1" />
<Label Grid.Row="0" Grid.Column="2" Text="Row 0, Col 2" />
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="Row 1, Col 0-1" />
<Label Grid.Row="1" Grid.Column="2" Text="Row 1, Col 2" />
<Label Grid.Row="2" Grid.Column="0" Grid.RowSpan="2" Text="Row 2-3, Col 0" />
<Label Grid.Row="2" Grid.Column="1" Text="Row 2, Col 1" />
<Label Grid.Row="3" Grid.Column="1" Text="Row 3, Col 1" />
</Grid>
4.3 数据绑定与 MVVM
数据绑定示例
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MyMauiApp.ViewModels"
x:Class="MyMauiApp.MainPage">
<ContentPage.BindingContext>
<viewmodels:MainViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout Padding="20">
<Label Text="{Binding WelcomeMessage}" FontSize="24" />
<Entry
Text="{Binding UserName}"
Placeholder="输入您的名字"
WidthRequest="250" />
<Button
Text="点击"
Command="{Binding GreetCommand}"
BackgroundColor="#007AFF"
TextColor="White" />
<Label Text="{Binding Greeting}" FontSize="18" />
</VerticalStackLayout>
</ContentPage>
ViewModel 实现
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.ComponentModel;
namespace MyMauiApp.ViewModels;
public partial class MainViewModel : ObservableObject
{
private string _userName;
private string _welcomeMessage = "欢迎使用 .NET MAUI";
private string _greeting;
public string WelcomeMessage
{
get => _welcomeMessage;
set => SetProperty(ref _welcomeMessage, value);
}
public string UserName
{
get => _userName;
set => SetProperty(ref _userName, value);
}
public string Greeting
{
get => _greeting;
set => SetProperty(ref _greeting, value);
}
[RelayCommand]
private void Greet()
{
Greeting = string.IsNullOrEmpty(UserName)
? "Hello, Guest!"
: $"Hello, {UserName}!";
}
}
五、平台适配与高级特性
5.1 平台特定代码
使用条件编译
// 共享代码中使用平台特定逻辑
public string GetPlatformName()
{
#if ANDROID
return "Android";
#elif IOS
return "iOS";
#elif MACCATALYST
return "macOS";
#elif WINDOWS
return "Windows";
#else
return "Unknown";
#endif
}
使用 Platform 类检测平台
using Microsoft.Maui.Devices;
// 检测当前平台
if (DeviceInfo.Platform == DevicePlatform.Android)
{
// Android 特定逻辑
}
else if (DeviceInfo.Platform == DevicePlatform.iOS)
{
// iOS 特定逻辑
}
5.2 依赖服务
定义接口
public interface IPlatformService
{
string GetUniqueDeviceId();
Task<Stream> GetImageStreamAsync(string url);
}
实现平台特定服务
// Android 实现
[assembly: Dependency(typeof(PlatformService_Android))]
namespace MyMauiApp.Droid.Services;
public class PlatformService_Android : IPlatformService
{
public string GetUniqueDeviceId()
{
return Android.OS.Build.Serial;
}
public async Task<Stream> GetImageStreamAsync(string url)
{
// Android 图片加载实现
var client = new HttpClient();
var response = await client.GetAsync(url);
return await response.Content.ReadAsStreamAsync();
}
}
使用依赖服务
// 在共享代码中调用
var deviceId = DependencyService.Get<IPlatformService>().GetUniqueDeviceId();
5.3 本地存储与数据访问
使用 SecureStorage 存储敏感数据
using Microsoft.Maui.Storage;
// 存储数据
await SecureStorage.SetAsync("auth_token", "your_token_here");
// 获取数据
var token = await SecureStorage.GetAsync("auth_token");
使用 SQLite 本地数据库
using Microsoft.EntityFrameworkCore;
using MyMauiApp.Models;
// 创建数据库上下文
public class AppDbContext : DbContext
{
public DbSet<TodoItem> TodoItems { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=todo.db");
}
}
// 使用数据库
using (var context = new AppDbContext())
{
var newItem = new TodoItem { Text = "学习 .NET MAUI", IsCompleted = false };
context.TodoItems.Add(newItem);
await context.SaveChangesAsync();
}
六、性能优化与最佳实践
6.1 UI 渲染优化
减少布局嵌套
复杂的嵌套布局会导致性能下降,应尽量简化:
<!-- 优化前 -->
<StackLayout>
<StackLayout>
<StackLayout>
<Label Text="Hello" />
</StackLayout>
</StackLayout>
</StackLayout>
<!-- 优化后 -->
<StackLayout>
<Label Text="Hello" />
</StackLayout>
使用 CollectionView 替代 ListView
CollectionView 支持虚拟化,仅渲染可见项:
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<VerticalStackLayout Padding="10">
<Label Text="{Binding Name}" FontSize="16" />
<Label Text="{Binding Description}" FontSize="12" />
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
6.2 网络请求优化
使用异步加载与缓存
using Microsoft.Maui.Controls;
using Microsoft.Maui.Essentials;
public async Task LoadDataAsync()
{
// 尝试从缓存加载
if (await Preferences.ContainsKeyAsync("cached_data"))
{
var cachedData = await Preferences.GetAsync("cached_data", "");
ProcessData(cachedData);
return;
}
// 从网络加载
var client = new HttpClient();
var response = await client.GetAsync("https://api.example.com/data");
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
await Preferences.SetAsync("cached_data", data, TimeSpan.FromHours(1));
ProcessData(data);
}
}
6.3 内存管理
优化图像资源
<!-- 使用合适分辨率的图像 -->
<Image
Source="icon_256.png"
WidthRequest="48"
HeightRequest="48"
Aspect="AspectFit" />
避免内存泄漏
// 错误示例:事件订阅未取消
private void Page_Loaded(object sender, EventArgs e)
{
MessagingCenter.Subscribe<MyViewModel>(this, "UpdateData", (msg) =>
{
// 处理数据更新
});
}
// 正确示例:取消订阅
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<MyViewModel>(this, "UpdateData");
}
七、打包与发布
7.1 生成 Android 应用
- 在 Visual Studio 中右键项目 > Properties > Android > Signing
- 创建签名密钥(JKS 文件)并填写相关信息
- 选择 Build > Archive 生成 APK/App Bundle
- 通过 Google Play Console 上传发布
7.2 生成 iOS 应用
- 确保已安装 Xcode 并配置 Apple 开发者账号
- 在 Visual Studio for Mac 中右键项目 > Properties > iOS Bundle Signing
- 选择证书和配置文件
- 选择 Build > Archive 生成 IPA 文件
- 通过 App Store Connect 上传发布
7.3 生成 Windows 应用
- 在 Visual Studio 中右键项目 > Properties > Package
- 配置应用信息、图标和发布设置
- 选择 Build > Archive 生成 MSIX 包
- 通过 Microsoft Store 发布或本地安装
八、实战项目:构建待办事项应用
8.1 项目结构
TodoApp/
├── Models/
│ └── TodoItem.cs # 数据模型
├── ViewModels/
│ └── TodoViewModel.cs # 视图模型
├── Views/
│ ├── TodoListPage.xaml # 待办事项列表页面
│ └── TodoEditPage.xaml # 待办事项编辑页面
├── Services/
│ └── TodoService.cs # 数据服务
└── App.xaml # 应用入口
8.2 核心实现
TodoItem 模型
namespace TodoApp.Models;
public class TodoItem
{
public int Id { get; set; }
public string Text { get; set; }
public bool IsCompleted { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
TodoService 数据服务
using Microsoft.EntityFrameworkCore;
using TodoApp.Models;
namespace TodoApp.Services;
public class TodoService
{
private readonly AppDbContext _dbContext;
public TodoService(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<List<TodoItem>> GetAllAsync()
{
return await _dbContext.TodoItems.OrderBy(t => t.CreatedAt).ToListAsync();
}
public async Task<TodoItem> AddAsync(TodoItem item)
{
_dbContext.TodoItems.Add(item);
await _dbContext.SaveChangesAsync();
return item;
}
public async Task UpdateAsync(TodoItem item)
{
_dbContext.TodoItems.Update(item);
await _dbContext.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var item = await _dbContext.TodoItems.FindAsync(id);
if (item != null)
{
_dbContext.TodoItems.Remove(item);
await _dbContext.SaveChangesAsync();
}
}
}
TodoListPage 视图
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:TodoApp.ViewModels"
x:Class="TodoApp.Views.TodoListPage"
Title="待办事项">
<ContentPage.BindingContext>
<viewmodels:TodoViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout Padding="10">
<Button
Text="添加新事项"
Command="{Binding AddCommand}"
BackgroundColor="#007AFF"
TextColor="White"
Margin="0,0,0,10" />
<CollectionView ItemsSource="{Binding Items}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<HorizontalStackLayout Padding="10" Spacing="10"
BackgroundColor="#F5F5F5"
Margin="0,0,0,5">
<CheckBox IsChecked="{Binding IsCompleted}"
Command="{Binding BindingContext.ToggleCommand, Source={RelativeSource AncestorType=CollectionView}}"
CommandParameter="{Binding .}" />
<Label Text="{Binding Text}"
FontSize="16"
HorizontalOptions="Center"
VerticalOptions="Center"
TextDecorations="{Binding IsCompleted, Converter={StaticResource CompletionTextDecorations}}" />
<Button Text="编辑"
Command="{Binding BindingContext.EditCommand, Source={RelativeSource AncestorType=CollectionView}}"
CommandParameter="{Binding .}"
BackgroundColor="#28a745"
TextColor="White"
WidthRequest="60" />
<Button Text="删除"
Command="{Binding BindingContext.DeleteCommand, Source={RelativeSource AncestorType=CollectionView}}"
CommandParameter="{Binding .}"
BackgroundColor="#dc3545"
TextColor="White"
WidthRequest="60" />
</HorizontalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
TodoViewModel 视图模型
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using TodoApp.Models;
using TodoApp.Services;
namespace TodoApp.ViewModels;
public partial class TodoViewModel : ObservableObject
{
private readonly TodoService _todoService;
private List<TodoItem> _items;
public TodoViewModel(TodoService todoService)
{
_todoService = todoService;
_items = new List<TodoItem>();
LoadItemsCommand.Execute(null);
}
public List<TodoItem> Items
{
get => _items;
set => SetProperty(ref _items, value);
}
[RelayCommand]
private async Task LoadItems()
{
IsBusy = true;
try
{
Items = await _todoService.GetAllAsync();
}
catch (Exception ex)
{
// 处理异常
Debug.WriteLine($"加载数据失败: {ex.Message}");
}
finally
{
IsBusy = false;
}
}
[RelayCommand]
private async Task Add()
{
var page = new TodoEditPage();
var result = await Shell.Current.Navigation.PushAsync(page);
if (result is TodoItem newItem)
{
await _todoService.AddAsync(newItem);
await LoadItems();
}
}
[RelayCommand]
private async Task Toggle(TodoItem item)
{
item.IsCompleted = !item.IsCompleted;
await _todoService.UpdateAsync(item);
OnPropertyChanged(nameof(Items));
}
[RelayCommand]
private async Task Edit(TodoItem item)
{
var page = new TodoEditPage(item);
var result = await Shell.Current.Navigation.PushAsync(page);
if (result is TodoItem updatedItem)
{
await _todoService.UpdateAsync(updatedItem);
await LoadItems();
}
}
[RelayCommand]
private async Task Delete(TodoItem item)
{
await _todoService.DeleteAsync(item.Id);
await LoadItems();
}
}
九、总结与进阶学习
9.1 总结
.NET MAUI 提供了一个强大的跨平台开发框架,通过统一的 API 和原生渲染能力,让开发者能够用 C# 高效构建 iOS、Android、Windows 和 macOS 应用。本文从基础架构、开发环境、UI 设计到平台适配和性能优化,全面介绍了 .NET MAUI 的核心概念和开发流程。
9.2 进阶学习资源
- 官方文档:https://learn.microsoft.com/zh-cn/dotnet/maui/
- GitHub 仓库:https://github.com/dotnet/maui
- 社区论坛:https://dev.to/、https://forums.xamarin.com/
- 书籍推荐:《.NET MAUI in Action》、《Building Cross-Platform Apps with .NET MAUI》
9.3 未来展望
随着 .NET MAUI 的持续发展,我们可以期待:
- 更强大的性能优化和渲染引擎
- 更多平台支持和集成能力
- 简化的开发工具和更友好的开发者体验
- 与 AI、机器学习等前沿技术的深度集成
通过掌握 .NET MAUI,开发者可以在跨平台应用开发领域获得更广阔的发展空间,为用户提供一致且高性能的应用体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



