第02章 - 环境搭建与快速开始
第02章:环境搭建与快速开始
2.1 开发环境准备
2.1.1 系统要求
Mapsui 支持在多种操作系统上进行开发:
| 操作系统 | 支持版本 | 可开发的目标平台 |
|---|---|---|
| Windows | Windows 10/11 | WPF, WinUI, Windows Forms, MAUI, Blazor, Avalonia |
| macOS | macOS 11+ | MAUI, Avalonia, iOS |
| Linux | Ubuntu 20.04+ | Avalonia, Blazor |
2.1.2 开发工具
推荐使用以下开发工具:
| 工具 | 版本要求 | 说明 |
|---|---|---|
| Visual Studio 2022 | 17.8+ | Windows 上的首选 IDE |
| Visual Studio for Mac | 最新版 | macOS 开发 |
| JetBrains Rider | 2023.3+ | 跨平台 IDE |
| VS Code | 最新版 | 轻量级编辑器 |
2.1.3 .NET SDK 安装
Mapsui 需要 .NET 6.0 或更高版本。推荐使用最新的 LTS 版本:
# 检查已安装的 .NET 版本
dotnet --list-sdks
# 下载 .NET SDK (从 https://dotnet.microsoft.com/download)
# Windows: 运行安装程序
# macOS: brew install --cask dotnet-sdk
# Linux: 参考官方文档安装
2.1.4 工作负载安装
根据目标平台,需要安装相应的工作负载:
# MAUI 开发
dotnet workload install maui
# iOS 开发 (仅 macOS)
dotnet workload install ios
# Android 开发
dotnet workload install android
# Blazor WebAssembly
dotnet workload install wasm-tools
2.2 各平台快速入门
2.2.1 MAUI 应用
步骤 1: 创建新的 .NET MAUI 应用:
dotnet new maui -n MapsuiMauiApp
cd MapsuiMauiApp
步骤 2: 添加 Mapsui.Maui NuGet 包:
dotnet add package Mapsui.Maui
步骤 3: 修改 MauiProgram.cs,添加 SkiaSharp 初始化:
using SkiaSharp.Views.Maui.Controls.Hosting;
namespace MapsuiMauiApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseSkiaSharp() // 关键:必须添加这行
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
return builder.Build();
}
}
步骤 4: 修改 MainPage.xaml.cs:
using Mapsui.UI.Maui;
using Mapsui.Tiling;
namespace MapsuiMauiApp;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
var mapControl = new MapControl();
mapControl.Map?.Layers.Add(OpenStreetMap.CreateTileLayer());
Content = mapControl;
}
}
步骤 5: 运行应用:
dotnet build
dotnet run
2.2.2 Avalonia 应用
准备工作: 安装 Avalonia 模板:
dotnet new install Avalonia.Templates
步骤 1: 创建新的 Avalonia 项目:
dotnet new avalonia.app -o MapsuiAvaloniaApp
cd MapsuiAvaloniaApp
步骤 2: 添加 Mapsui.Avalonia NuGet 包:
dotnet add package Mapsui.Avalonia
步骤 3: 修改 MainWindow.axaml:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mapsui="clr-namespace:Mapsui.UI.Avalonia;assembly=Mapsui.UI.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MapsuiAvaloniaApp.MainWindow"
Title="Mapsui Avalonia Demo">
<mapsui:MapControl x:Name="MyMapControl" />
</Window>
步骤 4: 修改 MainWindow.axaml.cs:
using Avalonia.Controls;
using Mapsui.Tiling;
namespace MapsuiAvaloniaApp;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyMapControl.Map?.Layers.Add(OpenStreetMap.CreateTileLayer());
}
}
步骤 5: 运行应用:
dotnet run
2.2.3 Blazor 应用
步骤 1: 创建新的 Blazor WebAssembly 应用:
dotnet new blazorwasm -o MapsuiBlazorApp
cd MapsuiBlazorApp
步骤 2: 添加 Mapsui.Blazor NuGet 包:
dotnet add package Mapsui.Blazor
步骤 3: 修改 Pages/Home.razor(或 Pages/Index.razor):
@page "/"
@using Mapsui.UI.Blazor
@using Mapsui.Tiling
<div class="container">
<div class="row">
<div class="col border rounded p-2 canvas-container">
<MapControlComponent @ref="_mapControl" />
</div>
</div>
</div>
<style>
.canvas-container canvas {
width: 100%;
height: 80vh;
}
</style>
@code {
private MapControl? _mapControl;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
if (firstRender)
{
_mapControl?.Map?.Layers.Add(OpenStreetMap.CreateTileLayer());
}
}
}
步骤 4: 运行应用:
dotnet run
故障排除: 如果文本无法正常显示,在项目文件中添加:
<ItemGroup>
<PackageReference Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2.3" GeneratePathProperty="true" />
<NativeFileReference Include="$(PKGHarfBuzzSharp_NativeAssets_WebAssembly)\build\netstandard1.0\libHarfBuzzSharp.a\3.1.12\libHarfBuzzSharp.a" />
</ItemGroup>
2.2.4 WPF 应用
步骤 1: 创建新的 WPF 项目:
dotnet new wpf -n MapsuiWpfApp -f net9.0
cd MapsuiWpfApp
步骤 2: 添加 Mapsui 相关 NuGet 包:
dotnet add package Mapsui
dotnet add package Mapsui.Wpf
步骤 3: 修改 MainWindow.xaml:
<Window x:Class="MapsuiWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mapsui="clr-namespace:Mapsui.UI.Wpf;assembly=Mapsui.UI.Wpf"
Title="Mapsui WPF Demo"
Height="600" Width="800">
<Grid>
<mapsui:MapControl x:Name="mapControl" />
</Grid>
</Window>
步骤 4: 修改 MainWindow.xaml.cs:
using System.Windows;
using Mapsui;
using Mapsui.Tiling;
namespace MapsuiWpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var map = new Map();
map.Layers.Add(OpenStreetMap.CreateTileLayer());
mapControl.Map = map;
}
}
}
步骤 5: 运行应用:
dotnet run
2.2.5 WinUI 应用
步骤 1: 在 Visual Studio 中创建新的 'Blank App, Packaged (WinUI 3 in Desktop)' 项目。
步骤 2: 添加 Mapsui.WinUI NuGet 包:
dotnet add package Mapsui.WinUI
步骤 3: 修改 MainPage.xaml:
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="MapsuiWinUIApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:winui="using:Mapsui.UI.WinUI">
<Grid>
<winui:MapControl x:Name="MyMap"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" />
</Grid>
</Page>
步骤 4: 修改 MainPage.xaml.cs:
using Microsoft.UI.Xaml.Controls;
using Mapsui.Tiling;
namespace MapsuiWinUIApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
MyMap.Map.Layers.Add(OpenStreetMap.CreateTileLayer());
}
}
}
2.2.6 Windows Forms 应用
步骤 1: 创建新的 Windows Forms 项目:
dotnet new winforms -n MapsuiWinFormsApp -f net9.0
cd MapsuiWinFormsApp
步骤 2: 添加 Mapsui.WindowsForms NuGet 包:
dotnet add package Mapsui.WindowsForms
步骤 3: 修改 Form1.cs:
using Mapsui.UI.WinForms;
using Mapsui.Tiling;
namespace MapsuiWinFormsApp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var mapControl = new MapControl
{
Dock = DockStyle.Fill
};
mapControl.Map.Layers.Add(OpenStreetMap.CreateTileLayer());
Controls.Add(mapControl);
}
}
}
步骤 4: 运行应用:
dotnet run
2.2.7 Uno Platform 应用
准备工作: 参考 Uno Platform 入门指南 配置开发环境。
步骤 1: 在 Visual Studio 中创建新的 'Uno Platform App' 项目。
步骤 2: 为所有目标平台添加 Mapsui.Uno.WinUI NuGet 包:
dotnet add package Mapsui.Uno.WinUI
步骤 3: 修改 MainPage.xaml:
<Page
x:Class="MapsuiUnoApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mapsui="clr-namespace:Mapsui.UI.WinUI;assembly=Mapsui.UI.Uno.WinUI">
<Grid>
<mapsui:MapControl x:Name="MyMap"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" />
</Grid>
</Page>
步骤 4: 修改 MainPage.xaml.cs:
using Windows.UI.Xaml.Controls;
using Mapsui.Tiling;
namespace MapsuiUnoApp
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
MyMap.Map.Layers.Add(OpenStreetMap.CreateTileLayer());
}
}
}
2.2.8 Eto Forms 应用
步骤 1: 创建新的 Eto.Forms 应用。
步骤 2: 将主项目的目标框架更新为 net9.0(Mapsui.Eto 需要 .NET 9.0+):
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
步骤 3: 添加 Mapsui.Eto NuGet 包:
dotnet add package Mapsui.Eto
步骤 4: 修改 MainForm.cs:
using Eto.Forms;
using Mapsui.UI.Eto;
using Mapsui.Tiling;
namespace MapsuiEtoApp
{
public class MainForm : Form
{
public MainForm()
{
Title = "Mapsui Eto Demo";
ClientSize = new Eto.Drawing.Size(800, 600);
var mapControl = new MapControl();
mapControl.Map.Layers.Add(OpenStreetMap.CreateTileLayer());
Content = mapControl;
}
}
}
2.2.9 .NET for Android 应用
步骤 1: 在 Visual Studio 中创建新的 'Blank App (Android)' 项目。
步骤 2: 添加 Mapsui.Android NuGet 包:
dotnet add package Mapsui.Android
步骤 3: 修改 Resources/layout/Main.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Mapsui.UI.Android.MapControl
android:id="@+id/mapcontrol"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
步骤 4: 修改 MainActivity.cs:
using Android.App;
using Android.OS;
using Mapsui;
using Mapsui.UI.Android;
using Mapsui.Tiling;
namespace MapsuiAndroidApp
{
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
var mapControl = FindViewById<MapControl>(Resource.Id.mapcontrol);
if (mapControl != null)
{
var map = new Map();
map.Layers.Add(OpenStreetMap.CreateTileLayer());
mapControl.Map = map;
}
}
}
}
2.2.10 .NET for iOS 应用
步骤 1: 在 Visual Studio for Mac 中创建新的 'Single View App' 项目。
步骤 2: 添加 Mapsui.iOS NuGet 包:
dotnet add package Mapsui.iOS
步骤 3: 修改 ViewController.cs:
using UIKit;
using Mapsui;
using Mapsui.UI.iOS;
using Mapsui.Tiling;
namespace MapsuiiOSApp
{
public partial class ViewController : UIViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
var mapControl = new MapControl(View!.Bounds);
var map = new Map();
map.Layers.Add(OpenStreetMap.CreateTileLayer());
mapControl.Map = map;
View = mapControl;
}
}
}
2.3 项目结构与配置
2.3.1 典型项目结构
一个标准的 Mapsui 应用项目结构如下:
MapsuiApp/
├── Properties/
│ └── launchSettings.json
├── Resources/ # 资源文件(图标、图片等)
│ ├── Images/
│ └── Styles/
├── Services/ # 服务类
│ ├── MapService.cs
│ └── DataService.cs
├── ViewModels/ # MVVM 视图模型
│ └── MainViewModel.cs
├── Views/ # 视图/页面
│ └── MainView.xaml
├── Layers/ # 自定义图层
│ └── CustomLayer.cs
├── Styles/ # 自定义样式
│ └── MapStyles.cs
├── App.xaml
├── App.xaml.cs
├── Program.cs
└── MapsuiApp.csproj
2.3.2 NuGet 包配置
根据项目需求选择合适的 NuGet 包组合:
<ItemGroup>
<!-- 核心包 -->
<PackageReference Include="Mapsui" Version="5.*" />
<!-- UI 框架特定包 (选择一个) -->
<PackageReference Include="Mapsui.Wpf" Version="5.*" />
<!-- 或 -->
<PackageReference Include="Mapsui.Maui" Version="5.*" />
<!-- 可选扩展 -->
<PackageReference Include="Mapsui.Nts" Version="5.*" />
<PackageReference Include="Mapsui.Extensions" Version="5.*" />
<PackageReference Include="Mapsui.ArcGIS" Version="5.*" />
</ItemGroup>
2.3.3 基本配置
创建一个配置类来管理地图设置:
public static class MapConfiguration
{
public static Map CreateDefaultMap()
{
var map = new Map
{
CRS = "EPSG:3857", // Web Mercator
BackColor = Color.FromString("#E0E0E0")
};
// 添加默认图层
map.Layers.Add(OpenStreetMap.CreateTileLayer());
// 添加默认小部件
map.Widgets.Add(new Mapsui.Widgets.ScaleBar.ScaleBarWidget(map)
{
MarginX = 10,
MarginY = 10
});
return map;
}
}
2.4 第一个完整示例
2.4.1 创建带标注的地图应用
下面是一个完整的 WPF 示例,展示如何创建一个带有多个标注点的地图应用:
MainWindow.xaml:
<Window x:Class="MapsuiDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mapsui="clr-namespace:Mapsui.UI.Wpf;assembly=Mapsui.UI.Wpf"
Title="Mapsui 示例应用"
Height="600" Width="900">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 工具栏 -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="5">
<Button Content="添加点" Click="AddPoint_Click" Margin="5"/>
<Button Content="清除" Click="Clear_Click" Margin="5"/>
<Button Content="回到中国" Click="ZoomToChina_Click" Margin="5"/>
</StackPanel>
<!-- 地图控件 -->
<mapsui:MapControl x:Name="mapControl" Grid.Row="1"/>
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.Windows;
using Mapsui;
using Mapsui.Layers;
using Mapsui.Nts;
using Mapsui.Projections;
using Mapsui.Styles;
using Mapsui.Tiling;
using Mapsui.Widgets.ScaleBar;
using Mapsui.Widgets.Zoom;
namespace MapsuiDemo
{
public partial class MainWindow : Window
{
private WritableLayer _pointLayer;
private int _pointCount = 0;
public MainWindow()
{
InitializeComponent();
InitializeMap();
}
private void InitializeMap()
{
var map = new Map();
// 添加 OpenStreetMap 底图
map.Layers.Add(OpenStreetMap.CreateTileLayer());
// 创建可写图层用于添加点
_pointLayer = new WritableLayer
{
Name = "Points",
Style = CreatePointStyle()
};
map.Layers.Add(_pointLayer);
// 添加缩放按钮小部件
map.Widgets.Add(new ZoomInOutWidget
{
MarginX = 20,
MarginY = 20
});
// 添加比例尺小部件
map.Widgets.Add(new ScaleBarWidget(map)
{
MarginX = 10,
MarginY = 40
});
mapControl.Map = map;
// 添加一些示例城市
AddCityPoints();
// 初始视图定位到中国
ZoomToChina();
// 处理地图点击事件
mapControl.Map.Info += Map_Info;
}
private void AddCityPoints()
{
var cities = new[]
{
(Name: "北京", Lon: 116.4, Lat: 39.9),
(Name: "上海", Lon: 121.5, Lat: 31.2),
(Name: "广州", Lon: 113.3, Lat: 23.1),
(Name: "深圳", Lon: 114.1, Lat: 22.5),
(Name: "杭州", Lon: 120.2, Lat: 30.3),
(Name: "成都", Lon: 104.1, Lat: 30.7)
};
foreach (var city in cities)
{
AddPoint(city.Lon, city.Lat, city.Name);
}
}
private void AddPoint(double lon, double lat, string name)
{
var point = SphericalMercator.FromLonLat(lon, lat).ToMPoint();
var feature = new PointFeature(point);
feature["name"] = name;
feature.Styles = new List<IStyle>
{
CreatePointStyle(),
CreateLabelStyle(name)
};
_pointLayer.Add(feature);
_pointLayer.DataHasChanged();
}
private static SymbolStyle CreatePointStyle()
{
return new SymbolStyle
{
SymbolScale = 0.5,
Fill = new Brush(Color.Red),
Outline = new Pen(Color.White, 2)
};
}
private static LabelStyle CreateLabelStyle(string text)
{
return new LabelStyle
{
Text = text,
ForeColor = Color.Black,
BackColor = new Brush(new Color(255, 255, 255, 200)),
Offset = new Offset(0, -25),
Font = new Font { Size = 12 }
};
}
private void AddPoint_Click(object sender, RoutedEventArgs e)
{
// 在随机位置添加点
var random = new Random();
var lon = 100 + random.NextDouble() * 35; // 中国经度范围
var lat = 20 + random.NextDouble() * 25; // 中国纬度范围
_pointCount++;
AddPoint(lon, lat, $"点 {_pointCount}");
}
private void Clear_Click(object sender, RoutedEventArgs e)
{
_pointLayer.Clear();
_pointLayer.DataHasChanged();
_pointCount = 0;
}
private void ZoomToChina_Click(object sender, RoutedEventArgs e)
{
ZoomToChina();
}
private void ZoomToChina()
{
// 中国的边界范围(经纬度)
var minLon = 73.0;
var maxLon = 135.0;
var minLat = 18.0;
var maxLat = 54.0;
// 转换为 Web Mercator 坐标
var min = SphericalMercator.FromLonLat(minLon, minLat);
var max = SphericalMercator.FromLonLat(maxLon, maxLat);
// 缩放到中国范围
mapControl.Map.Navigator.ZoomToBox(
new MRect(min.X, min.Y, max.X, max.Y),
MBoxFit.Fit
);
}
private void Map_Info(object? sender, MapInfoEventArgs e)
{
if (e.MapInfo?.Feature != null)
{
var name = e.MapInfo.Feature["name"]?.ToString() ?? "未知";
MessageBox.Show($"点击了: {name}", "地图信息");
}
}
}
}
2.4.2 运行效果
运行上述代码后,您将看到:
- OpenStreetMap 底图
- 中国主要城市的标注点
- 缩放控制按钮
- 比例尺
- 点击标注点会显示城市名称
2.5 常见问题与解决方案
2.5.1 MAUI 应用崩溃
问题: 应用启动时崩溃,报错 Catastrophic failure (0x8000FFFF) 或 Microsoft.Mapsui.Platform.HandlerNotFoundException。
解决方案: 确保在 MauiProgram.cs 中添加了 .UseSkiaSharp():
builder.UseMauiApp<App>().UseSkiaSharp();
2.5.2 Blazor 文本不显示
问题: Blazor 应用中文本/标签无法正常显示。
解决方案: 在项目文件中添加 HarfBuzz 引用:
<ItemGroup>
<PackageReference Include="HarfBuzzSharp.NativeAssets.WebAssembly"
Version="2.8.2.3"
GeneratePathProperty="true" />
<NativeFileReference Include="$(PKGHarfBuzzSharp_NativeAssets_WebAssembly)\build\netstandard1.0\libHarfBuzzSharp.a\3.1.12\libHarfBuzzSharp.a" />
</ItemGroup>
2.5.3 地图不显示
问题: 地图控件显示但没有内容。
解决方案:
- 检查是否添加了图层
- 检查网络连接(在线瓦片需要网络)
- 检查是否正确设置了 Map 属性
// 确保正确设置
mapControl.Map = map; // 而不是 mapControl.Map.Layers.Add(...)
2.5.4 坐标显示错误
问题: 添加的点位置不正确。
解决方案: 确保进行了正确的坐标转换:
// 如果使用经纬度,需要转换为 Web Mercator
var point = SphericalMercator.FromLonLat(lon, lat).ToMPoint();
2.5.5 Uno Platform 编译错误
问题: 遇到 Duplicate Attribute 错误。
解决方案: 在 .csproj 文件中添加:
<Target Name="FilterAnalyzers" BeforeTargets="CoreCompile">
<ItemGroup>
<FilteredAnalyzer Include="@(Analyzer->Distinct())" />
<Analyzer Remove="@(Analyzer)" />
<Analyzer Include="@(FilteredAnalyzer)" />
</ItemGroup>
</Target>
2.6 本章小结
本章详细介绍了 Mapsui 的环境搭建和各平台的快速入门:
- 开发环境准备:安装 .NET SDK、开发工具和必要的工作负载
- 各平台快速入门:
- MAUI、Avalonia、Blazor
- WPF、WinUI、Windows Forms
- Uno Platform、Eto Forms
- .NET for Android、.NET for iOS
- 项目结构与配置:合理组织项目结构和 NuGet 包配置
- 完整示例:创建带标注的交互式地图应用
- 常见问题解决:针对各平台的常见问题提供解决方案
在下一章中,我们将深入探讨 Mapsui 的核心架构与组件设计。
2.7 思考与练习
- 在您最熟悉的平台上创建一个 Mapsui 应用并显示 OpenStreetMap。
- 尝试添加一个自定义的标注点到地图上。
- 修改示例代码,将底图更换为其他地图服务(如 BingMaps)。
- 实现一个功能:点击地图时在点击位置添加一个标注。
- 比较不同平台上 Mapsui 应用的性能差异。

浙公网安备 33010602011771号