You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI运动App地图折线与ViewModel绑定实现问题

.NET MAUI运动应用:ViewModel绑定Map绘制路线解决方案

问题场景

开发.NET MAUI运动应用时,需从数据库获取跑步记录的经纬度列表,在地图上绘制折线展示路线。当前可单独完成数据获取和地图绘线,但无法结合:后台代码能操作routeMap控件,但无法访问ViewModel的数据;ViewModel的RelayCommand能拿到路线数据,却无法直接访问routeMap控件,实例化Map类报错。


解决方案

遵循MVVM原则,通过ViewModel暴露可绑定路线数据+页面监听数据变化更新地图的方式实现,避免ViewModel直接操作UI控件。

1. 修改ExerciseViewModel.cs

添加可观察的路线数据集合,移除直接操作Map的代码,只负责提供数据:

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DoradSmartphone.Models;
using DoradSmartphone.Services;
using Microsoft.Maui.Devices.Sensors;
using System.Collections.ObjectModel;

namespace DoradSmartphone.ViewModels
{
    public partial class ExerciseViewModel : BaseViewModel
    {
        private readonly ExerciseService exerciseService;

        public ObservableCollection<Exercise> Exercises { get; private set; } = new();
        
        // 添加可绑定的路线数据集合
        [ObservableProperty]
        ObservableCollection<Location> currentRouteLocations = new();

        public ExerciseViewModel(ExerciseService exerciseService)
        {
            Title = "Training Routes";
            this.exerciseService = exerciseService;
            _ = GetExerciseList();                                   
        }

        [ObservableProperty]
        bool isRefreshing;
        
        async Task GetExerciseList()
        {
            if (IsLoading) return;
            try
            {
                IsLoading = true;
                if (Exercises.Any()) Exercises.Clear();

                var exercices = exerciseService.GetExercises();
                foreach (var exercise in exercices) Exercises.Add(exercise);
            } catch(Exception ex) { 
                Console.WriteLine(ex.ToString());
                await Shell.Current.DisplayAlert("Error", "Failed to retrieve the exercice list", "Ok");
            }
            finally { 
                IsLoading = false; 
                isRefreshing= false;
             }
        }

        [RelayCommand]
        async Task ExerciseDetails(Exercise exercise)
        {
            if(exercise == null) return;

            // 获取路线数据并更新可绑定集合
            var routes = GetLocations(exercise.Id);
            CurrentRouteLocations.Clear();
            foreach(var loc in routes)
            {
                CurrentRouteLocations.Add(loc);
            }
        }

        public List<Location> GetLocations(int exerciseId)
        {
            if (exerciseId == 1)
            {
                return new List<Location>
                        {
                            new Location(35.6823324582143, 139.7620853729577),
                            new Location(35.679263477092704, 139.75773939496295),
                            new Location(35.68748054650018, 139.761486207315),
                            new Location(35.690745005825136, 139.7560362984393),
                            new Location(35.68966608916097, 139.75147199952355),
                            new Location(35.68427128680411, 139.7442168083328)
                        };
            }
            else if (exerciseId == 2)
            {
                return new List<Location>
                        {
                            new Location(35.6823324582143, 139.7620853729577),
                            new Location(35.679263477092704, 139.75773939496295),
                            new Location(35.68748054650018, 139.761486207315),
                            new Location(35.690745005825136, 139.7560362984393),
                            new Location(35.68966608916097, 139.75147199952355),
                            new Location(35.68427128680411, 139.7442168083328)
                        };
            }
            else
            {
                return new List<Location>
                        {
                            new Location(35.6823324582143, 139.7620853729577),
                            new Location(35.679263477092704, 139.75773939496295),
                            new Location(35.68748054650018, 139.761486207315),
                            new Location(35.690745005825136, 139.7560362984393),
                            new Location(35.68966608916097, 139.75147199952355),
                            new Location(35.68427128680411, 139.7442168083328)
                        };
            }
        }
    }
}

2. 修改ExercisePage.xaml.cs

添加监听ViewModel数据变化的逻辑,负责地图绘制操作:

using DoradSmartphone.Models;
using DoradSmartphone.ViewModels;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Maps;

namespace DoradSmartphone.Views;

public partial class ExercisePage : ContentPage
{
    private ExerciseViewModel _viewModel;

    public ExercisePage(ExerciseViewModel exerciseViewModel)
    {
        InitializeComponent();
        BindingContext = exerciseViewModel;
        _viewModel = exerciseViewModel;
        
        // 监听路线数据变化事件
        _viewModel.PropertyChanged += ViewModel_PropertyChanged;
    }

    private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        // 当CurrentRouteLocations更新时触发地图绘制
        if(e.PropertyName == nameof(_viewModel.CurrentRouteLocations))
        {
            UpdateMapRoute();
        }
    }

    private void UpdateMapRoute()
    {
        // 清除原有路线,避免重叠
        routeMap.MapElements.Clear();
        
        var locations = _viewModel.CurrentRouteLocations;
        if(locations.Count == 0) return;

        // 创建并配置折线
        var polyline = new Polyline
        {
            StrokeColor = Colors.Red,
            StrokeWidth = 12
        };

        foreach(var loc in locations)
        {
            polyline.Geopath.Add(loc);
        }

        // 添加折线到地图
        routeMap.MapElements.Add(polyline);

        // 移动地图到路线中心位置
        var center = locations[locations.Count / 2];
        routeMap.MoveToRegion(MapSpan.FromCenterAndRadius(center, Distance.FromMiles(1)));
    }
}

3. 验证绑定逻辑

确保XAML中已正确设置x:DataType ="viewmodel:ExerciseViewModel",无需额外修改布局部分。


关键说明

  • ViewModel仅负责数据提供,不直接操作UI控件,严格遵循MVVM分离原则
  • 通过PropertyChanged事件监听数据变化,在页面层统一处理地图绘制逻辑
  • 每次切换运动记录时,先清空原有路线再添加新折线,避免路线重叠

内容的提问来源于stack exchange,提问作者Luiz Carlos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 00:37:00