第15章 - 各平台集成实战

第15章:各平台集成实战

15.1 WPF 集成

15.1.1 MVVM 模式集成

// MapViewModel.cs
public class MapViewModel : INotifyPropertyChanged
{
    private Map _map;
    
    public Map Map
    {
        get => _map;
        set
        {
            _map = value;
            OnPropertyChanged();
        }
    }
    
    public MapViewModel()
    {
        InitializeMap();
    }
    
    private void InitializeMap()
    {
        Map = new Map();
        Map.Layers.Add(OpenStreetMap.CreateTileLayer());
        
        // 添加数据图层
        var dataLayer = new MemoryLayer { Name = "Data" };
        Map.Layers.Add(dataLayer);
    }
    
    public ICommand ZoomInCommand => new RelayCommand(() => Map.Navigator.ZoomIn());
    public ICommand ZoomOutCommand => new RelayCommand(() => Map.Navigator.ZoomOut());
    
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}
<!-- MainWindow.xaml -->
<Window x:Class="WpfApp.MainWindow"
        xmlns:mapsui="clr-namespace:Mapsui.UI.Wpf;assembly=Mapsui.UI.Wpf">
    <Window.DataContext>
        <local:MapViewModel/>
    </Window.DataContext>
    
    <Grid>
        <mapsui:MapControl Map="{Binding Map}"/>
        
        <StackPanel Orientation="Horizontal" 
                    HorizontalAlignment="Right" 
                    VerticalAlignment="Top"
                    Margin="10">
            <Button Content="+" Command="{Binding ZoomInCommand}" Width="30"/>
            <Button Content="-" Command="{Binding ZoomOutCommand}" Width="30"/>
        </StackPanel>
    </Grid>
</Window>

15.1.2 WPF 特有功能

// 获取 WPF 特有的 MapControl 属性
var wpfMapControl = mapControl as Mapsui.UI.Wpf.MapControl;

// 处理 WPF 鼠标事件
wpfMapControl.MouseLeftButtonDown += (s, e) =>
{
    var position = e.GetPosition(wpfMapControl);
    var worldPosition = wpfMapControl.Viewport.ScreenToWorld(
        new MPoint(position.X, position.Y)
    );
    // 处理点击
};

15.2 MAUI 集成

15.2.1 MAUI 应用结构

// MauiProgram.cs
using SkiaSharp.Views.Maui.Controls.Hosting;

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        .UseSkiaSharp()  // 必须添加
        .ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        });

    return builder.Build();
}

15.2.2 MAUI 地图页面

// MapPage.xaml.cs
public partial class MapPage : ContentPage
{
    private readonly MapControl _mapControl;
    
    public MapPage()
    {
        InitializeComponent();
        
        _mapControl = new MapControl();
        InitializeMap();
        Content = CreateLayout();
    }
    
    private void InitializeMap()
    {
        var map = _mapControl.Map;
        map.Layers.Add(OpenStreetMap.CreateTileLayer());
        
        // MAUI 平台的位置服务集成
        #if ANDROID || IOS
        RequestLocationPermission();
        #endif
    }
    
    private View CreateLayout()
    {
        return new Grid
        {
            Children =
            {
                _mapControl,
                new StackLayout
                {
                    Orientation = StackOrientation.Horizontal,
                    HorizontalOptions = LayoutOptions.End,
                    VerticalOptions = LayoutOptions.Start,
                    Margin = new Thickness(10),
                    Children =
                    {
                        new Button { Text = "+", Command = new Command(ZoomIn) },
                        new Button { Text = "-", Command = new Command(ZoomOut) }
                    }
                }
            }
        };
    }
    
    private void ZoomIn() => _mapControl.Map.Navigator.ZoomIn();
    private void ZoomOut() => _mapControl.Map.Navigator.ZoomOut();
    
    private async void RequestLocationPermission()
    {
        var status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
        if (status == PermissionStatus.Granted)
        {
            // 启用位置追踪
        }
    }
}

15.3 Avalonia 集成

15.3.1 Avalonia MVVM

// MainWindowViewModel.cs
public class MainWindowViewModel : ViewModelBase
{
    private Map _map;
    
    public Map Map
    {
        get => _map;
        set => this.RaiseAndSetIfChanged(ref _map, value);
    }
    
    public MainWindowViewModel()
    {
        Map = new Map();
        Map.Layers.Add(OpenStreetMap.CreateTileLayer());
    }
    
    public void ZoomIn() => Map.Navigator.ZoomIn();
    public void ZoomOut() => Map.Navigator.ZoomOut();
}
<!-- MainWindow.axaml -->
<Window xmlns="https://github.com/avaloniaui"
        xmlns:mapsui="clr-namespace:Mapsui.UI.Avalonia;assembly=Mapsui.UI.Avalonia">
    <Grid>
        <mapsui:MapControl Map="{Binding Map}"/>
        
        <StackPanel Orientation="Horizontal"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Top"
                    Margin="10">
            <Button Content="+" Command="{Binding ZoomInCommand}"/>
            <Button Content="-" Command="{Binding ZoomOutCommand}"/>
        </StackPanel>
    </Grid>
</Window>

15.4 Blazor 集成

15.4.1 Blazor WebAssembly

@page "/map"
@using Mapsui.UI.Blazor
@using Mapsui.Tiling

<div class="map-container">
    <MapControlComponent @ref="_mapControl" />
    
    <div class="map-controls">
        <button @onclick="ZoomIn">+</button>
        <button @onclick="ZoomOut">-</button>
    </div>
</div>

<style>
    .map-container {
        position: relative;
        width: 100%;
        height: 80vh;
    }
    
    .map-container canvas {
        width: 100%;
        height: 100%;
    }
    
    .map-controls {
        position: absolute;
        top: 10px;
        right: 10px;
    }
</style>

@code {
    private MapControl? _mapControl;
    
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender && _mapControl != null)
        {
            _mapControl.Map?.Layers.Add(OpenStreetMap.CreateTileLayer());
        }
    }
    
    private void ZoomIn() => _mapControl?.Map?.Navigator.ZoomIn();
    private void ZoomOut() => _mapControl?.Map?.Navigator.ZoomOut();
}

15.4.2 Blazor Server

@page "/map"
@using Mapsui.UI.Blazor
@inject IJSRuntime JSRuntime

<MapControlComponent @ref="_mapControl" />

@code {
    private MapControl? _mapControl;
    
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && _mapControl != null)
        {
            await InitializeMapAsync();
        }
    }
    
    private async Task InitializeMapAsync()
    {
        // Blazor Server 可能需要异步初始化
        await Task.Run(() =>
        {
            _mapControl?.Map?.Layers.Add(OpenStreetMap.CreateTileLayer());
        });
    }
}

15.5 Windows Forms 集成

15.5.1 基本集成

public partial class MapForm : Form
{
    private MapControl _mapControl;
    
    public MapForm()
    {
        InitializeComponent();
        InitializeMap();
    }
    
    private void InitializeMap()
    {
        _mapControl = new MapControl
        {
            Dock = DockStyle.Fill
        };
        
        _mapControl.Map.Layers.Add(OpenStreetMap.CreateTileLayer());
        
        Controls.Add(_mapControl);
        
        // 添加工具栏
        var toolStrip = new ToolStrip
        {
            Items =
            {
                new ToolStripButton("放大", null, (s, e) => _mapControl.Map.Navigator.ZoomIn()),
                new ToolStripButton("缩小", null, (s, e) => _mapControl.Map.Navigator.ZoomOut())
            }
        };
        Controls.Add(toolStrip);
    }
}

15.6 移动平台集成

15.6.1 Android 特定配置

// MainActivity.cs
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class MainActivity : Activity
{
    private MapControl _mapControl;
    
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.Main);
        
        _mapControl = FindViewById<MapControl>(Resource.Id.mapcontrol);
        
        var map = new Map();
        map.Layers.Add(OpenStreetMap.CreateTileLayer());
        _mapControl.Map = map;
        
        // 请求位置权限
        RequestLocationPermission();
    }
    
    private void RequestLocationPermission()
    {
        if (CheckSelfPermission(Manifest.Permission.AccessFineLocation) 
            != Permission.Granted)
        {
            RequestPermissions(
                new[] { Manifest.Permission.AccessFineLocation }, 
                1
            );
        }
    }
}

15.6.2 iOS 特定配置

// ViewController.cs
public class ViewController : UIViewController
{
    private MapControl _mapControl;
    
    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        
        _mapControl = new MapControl(View.Bounds);
        
        var map = new Map();
        map.Layers.Add(OpenStreetMap.CreateTileLayer());
        _mapControl.Map = map;
        
        View = _mapControl;
        
        // 请求位置权限
        RequestLocationAuthorization();
    }
    
    private void RequestLocationAuthorization()
    {
        var locationManager = new CLLocationManager();
        locationManager.RequestWhenInUseAuthorization();
    }
}

15.7 跨平台代码共享

15.7.1 共享业务逻辑

// Shared/MapService.cs (放在共享项目中)
public class MapService
{
    public Map CreateMap()
    {
        var map = new Map();
        
        // 添加底图
        map.Layers.Add(OpenStreetMap.CreateTileLayer());
        
        // 添加数据图层
        var dataLayer = new MemoryLayer { Name = "Data" };
        map.Layers.Add(dataLayer);
        
        // 添加小部件
        map.Widgets.Add(new ScaleBarWidget(map));
        
        return map;
    }
    
    public void AddMarker(Map map, double lon, double lat, string label)
    {
        var layer = map.Layers.FindLayer("Data") as MemoryLayer;
        if (layer == null) return;
        
        var point = SphericalMercator.FromLonLat(lon, lat).ToMPoint();
        var feature = new PointFeature(point);
        feature["label"] = label;
        
        // 更新图层
        var features = layer.Features?.ToList() ?? new List<IFeature>();
        features.Add(feature);
        layer.Features = features;
        layer.DataHasChanged();
    }
}

15.7.2 平台适配接口

// ILocationService.cs
public interface ILocationService
{
    Task<(double Lon, double Lat)?> GetCurrentLocationAsync();
    void StartTracking(Action<double, double> onLocationChanged);
    void StopTracking();
}

// Android 实现
public class AndroidLocationService : ILocationService
{
    // Android 特定实现
}

// iOS 实现
public class iOSLocationService : ILocationService
{
    // iOS 特定实现
}

15.8 本章小结

本章详细介绍了 Mapsui 在各平台的集成:

  1. WPF 集成:MVVM 模式、数据绑定
  2. MAUI 集成:跨平台移动应用
  3. Avalonia 集成:跨平台桌面应用
  4. Blazor 集成:Web 应用
  5. Windows Forms 集成:传统桌面应用
  6. 移动平台:Android 和 iOS 特定配置
  7. 跨平台代码共享:业务逻辑共享和平台适配

在下一章中,我们将通过实战案例综合运用所学知识。

15.9 思考与练习

  1. 创建一个 MAUI 应用,支持 Android 和 iOS 位置追踪。
  2. 实现一个 Blazor 地图应用,支持地图搜索功能。
  3. 使用 Avalonia 创建一个支持 Windows、macOS 和 Linux 的地图应用。
  4. 设计一个跨平台的地图 SDK,抽象平台差异。
  5. 实现一个离线地图功能,在各平台上都能工作。

posted @ 2026-01-09 00:40  我才是银古  阅读(20)  评论(0)    收藏  举报