xamarin .Net毛伊岛谷歌Map折线绘制路线功能

ma8fv8wu  于 2023-06-03  发布在  .NET
关注(0)|答案(1)|浏览(216)

我有这样的情况:我正在构建一个.net Maui智能手机体育应用程序,它可以获取跑步活动的经纬度列表(新的Location类),并在Map中绘制一条线(polyline)来显示路线。我可以从数据库中获取练习列表,也可以在Map中绘制折线,问题是我不能同时做这两件事,因为我不知道如何在ViewModel类中绑定Map函数。
下面是ExercisePage.xaml的xaml代码:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DoradSmartphone.Views.ExercisePage"
             xmlns:model="clr-namespace:DoradSmartphone.Models"
             xmlns:viewmodel="clr-namespace:DoradSmartphone.ViewModels"
             xmlns:maps="clr-namespace:Microsoft.Maui.Controls.Maps;assembly=Microsoft.Maui.Controls.Maps"
             xmlns:sensors="clr-namespace:Microsoft.Maui.Devices.Sensors;assembly=Microsoft.Maui.Essentials"
             x:DataType ="viewmodel:ExerciseViewModel"
             Title="{Binding Title}">



    <Grid Padding="5" Margin="5" RowSpacing="5" ColumnSpacing="3">
        <Grid.RowDefinitions>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="150"/>
        </Grid.RowDefinitions>

        <maps:Map Grid.Row="0" x:Name="routeMap" VerticalOptions="CenterAndExpand" Grid.ColumnSpan="3" HeightRequest="400" IsZoomEnabled="False" IsEnabled="False">
            <x:Arguments>
                <MapSpan>
                    <x:Arguments>
                        <sensors:Location>
                            <x:Arguments>
                                <x:Double>38.744418137669875</x:Double>
                                <x:Double>-9.128544160596851</x:Double>
                            </x:Arguments>
                        </sensors:Location>
                        <x:Double>0.7</x:Double>
                        <x:Double>0.7</x:Double>
                    </x:Arguments>
                </MapSpan>
            </x:Arguments>
        </maps:Map>

        <CarouselView ItemsSource="{Binding Exercises}" Grid.Row="1" PeekAreaInsets="100">
            <CarouselView.ItemTemplate>
                <DataTemplate x:DataType="model:Exercise">
                    <Frame HeightRequest="90" Margin="5">
                        <Frame.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:ExerciseViewModel}}, Path=ExerciseDetailsCommand}
                                            " CommandParameter="{Binding .}"></TapGestureRecognizer>
                        </Frame.GestureRecognizers>
                        <HorizontalStackLayout Padding="10" Spacing="5" >
                            <Label Text="{Binding Id}"></Label>
                            <Label Text="{Binding Date}"></Label>
                        </HorizontalStackLayout>
                    </Frame>
                </DataTemplate>
            </CarouselView.ItemTemplate>
        </CarouselView>
    </Grid>
</ContentPage>

正如你所看到的,我把我的Map名声明为routeMap,第一个位置只是从某处开始。我还为CarouselView中的练习列表的DataBinding声明了我的模型和视图模型。点击功能工作正常,并带我到一个名为ExerciseDetailsPage的新视图。
这是ExercisePage.xaml.cs背后的代码

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

namespace DoradSmartphone.Views;

public partial class ExercisePage : ContentPage
{
    public ExercisePage(ExerciseViewModel exerciseViewModel)
    {
        InitializeComponent();
        BindingContext = exerciseViewModel;
    }

    
    private void OnTapGestureRouteUpdate(object sender, EventArgs e)
    {
        var route = new Polyline
        {
            StrokeColor = Colors.Red,
            StrokeWidth = 12,
            Geopath =
            {
                new Location(38.70061856336034 , -8.957381918676203 ),
                new Location(38.70671683905933 , -8.945225024701308 ),
                new Location(38.701985630081595, -8.944503277546072 ),
                new Location(38.701872978433386, -8.940750192338834 ),
                new Location(38.71054663609023 , -8.939162348597312 ),
                new Location(38.717755109243214, -8.942193686649311 ),
                new Location(38.7435419727561  , -8.928480490699792 ),
                new Location(38.78327379379296 , -8.880556478454272 ),
                new Location(38.925473761602376, -8.881999972299806 ),
                new Location(38.93692729913667 , -8.869585920414709 ),
                new Location(38.93493556584553 , -8.86536198145887  )
            }
        };
        routeMap.MoveToRegion(
            MapSpan.FromCenterAndRadius(
                new Location(38.93479161472441, -8.865352563545757), Distance.FromMiles(1)));
        // Add the polyline to the map
        routeMap.MapElements.Add(route);
    }
}

如果我将实际的点击功能更改为这个点击事件,我可以在Map中绘制任何线条和其他内容,因为我可以读取xaml代码中定义的map名称。但是在这个codebehind类中,我无法访问ViewModel、Services或Model类。
这是我的ExerciseViewModel.cs类:

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DoradSmartphone;
using DoradSmartphone.Models;
using DoradSmartphone.Services;
using DoradSmartphone.Views;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Maps;
using System.Collections.ObjectModel;

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

        public ObservableCollection<Exercise> Exercises { get; private set; } = 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);

            DrawRoutes(routes);
        }

        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)
                        };
            }
        }

        private void DrawRoutes(List<Location> routes)
        {
            var polylines = new Polyline
            {
                StrokeColor = Colors.Red,
                StrokeWidth = 12,
            };

            foreach(var route in routes)
            {
                polylines.Geopath.Add(route);
            }                        
            
            routeMap.MoveToRegion(
                MapSpan.FromCenterAndRadius(
                    routes.FirstOrDefault(), Distance.FromMiles(1)));
            // Add the polyline to the map
            routeMap.MapElements.Add(polylines);
        }
    }
}

这个类继承了继承ObservableObject的BaseViewModel,并且具有所有其他类的一些公共属性。在ExerciseViewModel中,我的RelayCommand与捕获锻炼对象并添加路线的tap特性相关,但我无法访问routeMap对象。我也尝试在我的viewmodel类中声明一个Map类,但是我总是得到错误,我不能创建一个静态类的示例。
这是我的MauiProgram.cs,以防出现问题:

using DoradSmartphone.Data;
using DoradSmartphone.Services;
using DoradSmartphone.ViewModels;
using DoradSmartphone.Views;

namespace DoradSmartphone;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()       
            .UseMauiMaps()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        builder.Services.AddSingleton<DatabaseConn>();
        builder.Services.AddScoped<IRepository, DatabaseConn>();

        builder.Services.AddSingleton<MainPage>();
        builder.Services.AddSingleton<UserPage>();
        builder.Services.AddSingleton<LoginPage>();        
        builder.Services.AddSingleton<LoadingPage>();
        builder.Services.AddSingleton<ExercisePage>();
        builder.Services.AddSingleton<DashboardPage>();
        builder.Services.AddSingleton<ExerciseDetailsPage>();

        builder.Services.AddSingleton<UserService>();
        builder.Services.AddSingleton<LoginService>();        
        builder.Services.AddSingleton<ExerciseService>();
        builder.Services.AddSingleton<DashboardService>();

        builder.Services.AddSingleton<UserViewModel>();
        builder.Services.AddSingleton<LoginViewModel>();        
        builder.Services.AddSingleton<LoadingViewModel>();
        builder.Services.AddSingleton<ExerciseViewModel>();
        builder.Services.AddSingleton<DashboardViewModel>();
        builder.Services.AddTransient<ExerciseDetailsViewModel>();

        return builder.Build();
    }
}

感谢您的评分

kwvwclae

kwvwclae1#

不幸的是,MapElements不是可绑定的属性。但是,您可以通过以下几种方法来解决这个问题
例如,在VM中创建一个返回路由数据的公共方法

public Polyline GetRouteData()
{
    var polylines = new Polyline
        {
            StrokeColor = Colors.Red,
            StrokeWidth = 12,
        };

        foreach(var route in routes)
        {
            polylines.Geopath.Add(route);
        } 

  return polylines; 
}

然后在后面的代码中,首先创建对VM的类引用

ExerciseViewModel ViewModel;

public ExercisePage(ExerciseViewModel exerciseViewModel)
{
    InitializeComponent();
    BindingContext = ViewModel = exerciseViewModel;
}

则您的代码可以从VM获取更新Map所需的数据

routeMap.MapElements.Add(ViewModel.GetRouteData());

相关问题