.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
相关产品推荐
相关产品推荐

