[Maps] Add Windows map handler based on Webview (#604)

* [Maps] Add windows maps draft

* Fix the csproj

* Use Mapper.ModifyMapping

* Move stuff around

* Add init code

* [Maps] Fix MAUI package version

* [Maps] Remove extra reference that isn't needed

* [Maps] Add implementation IsShowingUser

* [Maps] Update visible region

* Fix initial move to region firing before html page load

* Tidy code

* Update CommunityToolkit.Maui.Sample.csproj

* Add basic Map sample

* Add Pins sample page

* Map pins

* fix indention

* Update pipeline, code cleanup

* Remove duplicates

* Fix maps samples

---------

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
Co-authored-by: Gerald Versluis <gerald@verslu.is>
Co-authored-by: Vladislav Antonyuk <33021114+VladislavAntonyuk@users.noreply.github.com>
Co-authored-by: Vladislav Antonyuk <vlad.antonyuk@gmail.com>
This commit is contained in:
Rui Marinho 2023-05-31 14:35:38 +01:00 коммит произвёл GitHub
Родитель 642b77849d
Коммит 61a64de37e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
17 изменённых файлов: 753 добавлений и 2 удалений

Просмотреть файл

@ -4,6 +4,7 @@ variables:
CurrentSemanticVersion: '$(CurrentSemanticVersionBase)-preview$(PreviewNumber)'
NugetPackageVersion: '$(CurrentSemanticVersion)'
NugetPackageVersionMediaElement: '$(CurrentSemanticVersion)'
NugetPackageVersionMaps: '$(CurrentSemanticVersion)'
TOOLKIT_NET_VERSION: '7.0.200'
LATEST_NET_VERSION: '7.0.x'
PathToLibrarySolution: 'src/CommunityToolkit.Maui.sln'
@ -11,6 +12,7 @@ variables:
PathToCommunityToolkitCsproj: 'src/CommunityToolkit.Maui/CommunityToolkit.Maui.csproj'
PathToCommunityToolkitCoreCsproj: 'src/CommunityToolkit.Maui.Core/CommunityToolkit.Maui.Core.csproj'
PathToCommunityToolkitMediaElementCsproj: 'src/CommunityToolkit.Maui.MediaElement/CommunityToolkit.Maui.MediaElement.csproj'
PathToCommunityToolkitMapsCsproj: 'src/CommunityToolkit.Maui.Maps/CommunityToolkit.Maui.Maps.csproj'
PathToCommunityToolkitSampleCsproj: 'samples/CommunityToolkit.Maui.Sample/CommunityToolkit.Maui.Sample.csproj'
PathToCommunityToolkitUnitTestCsproj: 'src/CommunityToolkit.Maui.UnitTests/CommunityToolkit.Maui.UnitTests.csproj'
PathToCommunityToolkitAnalyzersCsproj: 'src/CommunityToolkit.Maui.Analyzers/CommunityToolkit.Maui.Analyzers.csproj'
@ -138,6 +140,16 @@ jobs:
displayName: Set NuGet Version to Tag Number
condition: and(startsWith(variables['Build.SourceBranch'], 'refs/tags/'), endsWith(variables['Build.SourceBranch'], '-mediaelement')) # Only run this step when a Tag has triggered the CI Pipeline
# if this is a tagged build for CommunityToolkit.Maui.Maps, then update the version number
- powershell: |
$buildSourceBranch = "$(Build.SourceBranch)"
$tagVersion = $buildSourceBranch.Substring($buildSourceBranch.LastIndexOf("/") + 1)
$tagVersion = $tagVersion.Substring(0, $tagVersion.LastIndexOf("-"))
Write-Host("Branch = $buildSourceBranch, Version = $tagVersion");
Write-Host ("##vso[task.setvariable variable=NugetPackageVersionMaps;]$tagVersion")
displayName: Set NuGet Version to Tag Number
condition: and(startsWith(variables['Build.SourceBranch'], 'refs/tags/'), endsWith(variables['Build.SourceBranch'], '-maps')) # Only run this step when a Tag has triggered the CI Pipeline
# if this is a PR build, then update the version number
- powershell: |
$prNumber = $env:System_PullRequest_PullRequestNumber
@ -238,6 +250,11 @@ jobs:
inputs:
script: 'dotnet pack $(PathToCommunityToolkitMediaElementCsproj) -c Release -p:PackageVersion=$(NugetPackageVersionMediaElement)'
- task: CmdLine@2
displayName: 'Pack CommunityToolkit.Maui.Maps NuGet'
inputs:
script: 'dotnet pack $(PathToCommunityToolkitMapsCsproj) -c Release -p:PackageVersion=$(NugetPackageVersionMaps)'
# check vulnerabilities
- powershell: |
dotnet list $(PathToLibrarySolution) package --vulnerable --include-transitive | findstr /S /c:"has the following vulnerable packages";
@ -286,6 +303,18 @@ jobs:
$filter = "CommunityToolkit.Maui.MediaElement.[0-9]+.[0-9]+.[0-9]+(-[a-zA-Z0-9]*)*.nupkg"
Get-ChildItem -Path $source -Recurse | Where-Object { $_.Name -match $filter } | Copy-Item -Destination "$(Build.ArtifactStagingDirectory)"
pwsh: true
# Copy CommunityToolkit.Maui.Maps package
- task: PowerShell@2
condition: and(eq(variables['Agent.OS'], 'Windows_NT'), startsWith(variables['Build.SourceBranch'], 'refs/tags/'), endsWith(variables['Build.SourceBranch'], '-maps')) # Only run this step on Windows and when it's a tagged build and the tag ends with -maps
displayName: 'Copy CommunityToolkit.Maui.Maps NuGet Package to Staging Directory'
inputs:
targetType: 'inline'
script: |
$source = ".\src"
$filter = "CommunityToolkit.Maui.Maps.[0-9]+.[0-9]+.[0-9]+(-[a-zA-Z0-9]*)*.nupkg"
Get-ChildItem -Path $source -Recurse | Where-Object { $_.Name -match $filter } | Copy-Item -Destination "$(Build.ArtifactStagingDirectory)"
pwsh: true
# Sign NuGet Packages
- task: PowerShell@2

Просмотреть файл

@ -33,6 +33,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9F7D54C0
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Maui.Analyzers.UnitTests", "..\src\CommunityToolkit.Maui.Analyzers.UnitTests\CommunityToolkit.Maui.Analyzers.UnitTests.csproj", "{60B976B2-F3FA-494E-A28B-7BED2EAE990E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui.Maps", "..\src\CommunityToolkit.Maui.Maps\CommunityToolkit.Maui.Maps.csproj", "{6D2CACB2-9AAA-4F1F-B480-750F23D5FEA2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Maui.MediaElement", "..\src\CommunityToolkit.Maui.MediaElement\CommunityToolkit.Maui.MediaElement.csproj", "{A651D248-B102-4B8B-90C2-3C4C28425EFF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Maui.MediaElement.Analyzers", "..\src\CommunityToolkit.Maui.MediaElement.Analyzers\CommunityToolkit.Maui.MediaElement.Analyzers.csproj", "{85B875BD-62F6-4EC3-BCFF-4DC25E94BCAE}"
@ -81,6 +83,10 @@ Global
{60B976B2-F3FA-494E-A28B-7BED2EAE990E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60B976B2-F3FA-494E-A28B-7BED2EAE990E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60B976B2-F3FA-494E-A28B-7BED2EAE990E}.Release|Any CPU.Build.0 = Release|Any CPU
{6D2CACB2-9AAA-4F1F-B480-750F23D5FEA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D2CACB2-9AAA-4F1F-B480-750F23D5FEA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D2CACB2-9AAA-4F1F-B480-750F23D5FEA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D2CACB2-9AAA-4F1F-B480-750F23D5FEA2}.Release|Any CPU.Build.0 = Release|Any CPU
{A651D248-B102-4B8B-90C2-3C4C28425EFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A651D248-B102-4B8B-90C2-3C4C28425EFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A651D248-B102-4B8B-90C2-3C4C28425EFF}.Release|Any CPU.ActiveCfg = Release|Any CPU

Просмотреть файл

@ -113,8 +113,13 @@ public partial class AppShell : Shell
CreateViewModelMapping<AvatarViewSizesPage, AvatarViewSizesViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<DrawingViewPage, DrawingViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<ExpanderPage, ExpanderViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<BasicMapsPage, BasicMapsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<MapsPinsPage, MapsPinsViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<LazyViewPage, LazyViewViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<MediaElementPage, MediaElementViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<MultiplePopupPage, MultiplePopupViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<PopupAnchorPage, PopupAnchorViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),
CreateViewModelMapping<PopupPositionPage, PopupPositionViewModel, ViewsGalleryPage, ViewsGalleryViewModel>(),

Просмотреть файл

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0-ios;net7.0-android;net7.0-maccatalyst</TargetFrameworks>
@ -69,6 +69,7 @@
<ProjectReference Include="..\..\src\CommunityToolkit.Maui.Analyzers.CodeFixes\CommunityToolkit.Maui.Analyzers.CodeFixes.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\CommunityToolkit.Maui.MediaElement\CommunityToolkit.Maui.MediaElement.csproj" />
<ProjectReference Include="..\..\src\CommunityToolkit.Maui\CommunityToolkit.Maui.csproj" />
<ProjectReference Include="..\..\src\CommunityToolkit.Maui.Maps\CommunityToolkit.Maui.Maps.csproj" />
</ItemGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))=='android'">

Просмотреть файл

@ -21,6 +21,7 @@ using CommunityToolkit.Maui.Sample.ViewModels.ImageSources;
using CommunityToolkit.Maui.Sample.ViewModels.Layouts;
using CommunityToolkit.Maui.Sample.ViewModels.Views;
using CommunityToolkit.Maui.Sample.ViewModels.Views.AvatarView;
using CommunityToolkit.Maui.Maps;
using CommunityToolkit.Maui.Storage;
using Microsoft.Extensions.Logging;
using Polly;
@ -46,6 +47,7 @@ public static class MauiProgram
#endif
.UseMauiCommunityToolkitMarkup()
.UseMauiCommunityToolkitMediaElement()
.UseMauiCommunityToolkitMaps("") // You should add your own key here from bingmapsportal.com
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
@ -53,7 +55,7 @@ public static class MauiProgram
});
builder.Services.AddHttpClient<ByteArrayToImageSourceConverterViewModel>()
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(3, SleepDurationProvider));
.AddTransientHttpErrorPolicy(policyBuilder => policyBuilder.WaitAndRetryAsync(3, SleepDurationProvider));
builder.Services.AddSingleton<PopupSizeConstants>();
@ -172,8 +174,13 @@ public static class MauiProgram
// Add Views Pages + ViewModels
services.AddTransientWithShellRoute<DrawingViewPage, DrawingViewViewModel>();
services.AddTransientWithShellRoute<ExpanderPage, ExpanderViewModel>();
services.AddTransientWithShellRoute<BasicMapsPage, BasicMapsViewModel>();
services.AddTransientWithShellRoute<MapsPinsPage, MapsPinsViewModel>();
services.AddTransientWithShellRoute<LazyViewPage, LazyViewViewModel>();
services.AddTransientWithShellRoute<MediaElementPage, MediaElementViewModel>();
services.AddTransientWithShellRoute<MultiplePopupPage, MultiplePopupViewModel>();
services.AddTransientWithShellRoute<PopupAnchorPage, PopupAnchorViewModel>();
services.AddTransientWithShellRoute<PopupPositionPage, PopupPositionViewModel>();

Просмотреть файл

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
xmlns:vm="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views"
x:Class="CommunityToolkit.Maui.Sample.Pages.Views.BasicMapsPage"
x:DataType="vm:BasicMapsViewModel"
x:TypeArguments="vm:BasicMapsViewModel"
Title="Windows Map">
<Grid RowDefinitions="Auto, Auto, Auto, Auto, *">
<HorizontalStackLayout Grid.Row="0">
<Switch x:Name="ShowUserLocationSwitch" Margin="5"/>
<Label Text="Show user" VerticalOptions="Center"/>
<Switch x:Name="ShowTrafficSwitch" Margin="5"/>
<Label Text="Show traffic" VerticalOptions="Center"/>
</HorizontalStackLayout>
<HorizontalStackLayout Grid.Row="1">
<Label Text="MapType" VerticalOptions="Center"/>
<Picker x:Name="MapTypePicker" Margin="5" SelectedIndex="0" SelectedIndexChanged="MapTypePicker_OnSelectedIndexChanged">
<Picker.Items>
<x:String>Hybrid</x:String>
<x:String>Satellite</x:String>
<x:String>Street</x:String>
</Picker.Items>
</Picker>
</HorizontalStackLayout>
<HorizontalStackLayout Grid.Row="2">
<Switch x:Name="ZoomEnabledSwitch" Margin="5" IsToggled="True"/>
<Label Text="Zoom enabled" VerticalOptions="Center"/>
<Switch x:Name="ScrollEnabledSwitch" Margin="5" IsToggled="True"/>
<Label Text="Scroll enabled" VerticalOptions="Center"/>
</HorizontalStackLayout>
<Button Text="Move to region" Clicked="Button_OnClicked" Grid.Row="3"/>
<Map x:Name="BasicMap" Grid.Row="4"
IsShowingUser="{Binding IsToggled, Source={x:Reference ShowUserLocationSwitch}}"
IsTrafficEnabled="{Binding IsToggled, Source={x:Reference ShowTrafficSwitch}}"
IsZoomEnabled="{Binding IsToggled, Source={x:Reference ZoomEnabledSwitch}}"
IsScrollEnabled="{Binding IsToggled, Source={x:Reference ScrollEnabledSwitch}}"/>
</Grid>
</pages:BasePage>

Просмотреть файл

@ -0,0 +1,19 @@
using CommunityToolkit.Maui.Sample.ViewModels.Views;
using Microsoft.Maui.Maps;
namespace CommunityToolkit.Maui.Sample.Pages.Views;
public partial class BasicMapsPage : BasePage<BasicMapsViewModel>
{
public BasicMapsPage(BasicMapsViewModel mapsViewModel) : base(mapsViewModel) => InitializeComponent();
private void MapTypePicker_OnSelectedIndexChanged(object? sender, EventArgs e)
{
BasicMap.MapType = (MapType)MapTypePicker.SelectedIndex;
}
private void Button_OnClicked(object? sender, EventArgs e)
{
BasicMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Location(50, 6), Distance.FromKilometers(1)));
}
}

Просмотреть файл

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:CommunityToolkit.Maui.Sample.Pages"
xmlns:vm="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views"
x:DataType="vm:MapsPinsViewModel"
x:TypeArguments="vm:MapsPinsViewModel"
x:Class="CommunityToolkit.Maui.Sample.Pages.Views.MapsPinsPage"
Title="Windows Maps Pins Sample">
<Grid RowDefinitions="Auto, *">
<HorizontalStackLayout Grid.Row="0">
<Button Text="Init region" Clicked="InitRegion_OnClicked" />
<Button Text="Add Pin" Clicked="AddPin_Clicked" />
<Button Text="Remove Pin" Clicked="RemovePin_Clicked" />
<Button Text="Add 10 Pins" Clicked="Add10Pins_Clicked" />
</HorizontalStackLayout>
<Map x:Name="PinsMap" Grid.Row="1" />
</Grid>
</pages:BasePage>

Просмотреть файл

@ -0,0 +1,117 @@
using Microsoft.Maui.Controls.Maps;
using CommunityToolkit.Maui.Sample.ViewModels.Views;
using Microsoft.Maui.Maps;
namespace CommunityToolkit.Maui.Sample.Pages.Views;
public partial class MapsPinsPage : BasePage<MapsPinsViewModel>
{
private readonly Random locationRandomSeed = new();
private int locationIncrement = 0;
// TODO generate actual random pins
private readonly Location[] randomLocations =
{
new Location(51.8833333333333, 176.65),
new Location(21.3166666666667, 157.833333333333),
new Location(71.3, 156.766666666667),
new Location(19.7, 155.083333333333),
new Location(61.2166666666667, 149.9),
new Location(70.2, 148.516666666667),
new Location(64.85, 147.716666666667),
new Location(57.05, 135.333333333333),
new Location(60.7166666666667, 135.05),
new Location(58.3, 134.416666666667),
new Location(69.45, 133.033333333333),
new Location(48.4333333333333, 123.366666666667),
new Location(49.25, 123.1),
new Location(45.5166666666667, 122.683333333333),
new Location(37.7833333333333, 122.416666666667),
new Location(47.6166666666667, 122.333333333333),
new Location(38.55, 121.466666666667),
new Location(50.6833333333333, 120.333333333333),
new Location(39.5333333333333, 119.816666666667),
new Location(34.4333333333333, 119.716666666667),
new Location(49.8833333333333, 119.5),
new Location(55.1666666666667, 118.8),
new Location(34.05, 118.25),
new Location(33.95, 117.4),
new Location(32.7166666666667, 117.166666666667),
new Location(32.5333333333333, 117.033333333333),
new Location(31.85, 116.6),
new Location(43.6166666666667, 116.2),
new Location(32.6666666666667, 115.466666666667),
new Location(36.1833333333333, 115.133333333333),
new Location(62.45, 114.4),
new Location(51.05, 114.066666666667),
new Location(53.5333333333333, 113.5),
new Location(33.45, 112.066666666667),
new Location(46.6, 112.033333333333),
};
public MapsPinsPage(MapsPinsViewModel mapsPinsViewModel) : base(mapsPinsViewModel)
{
InitializeComponent();
}
void AddPin_Clicked(object sender, EventArgs e)
{
AddPin();
}
void RemovePin_Clicked(object sender, EventArgs e)
{
if (PinsMap.Pins.Count > 0)
{
PinsMap.Pins.RemoveAt(PinsMap.Pins.Count - 1);
locationIncrement--;
}
}
void Add10Pins_Clicked(object sender, EventArgs e)
{
for (int i = 0; i <= 10; i++)
{
AddPin();
}
}
void AddPin()
{
PinsMap.Pins.Add(new Pin()
{
Label = $"Location {locationIncrement++}",
Location = randomLocations[locationRandomSeed.Next(0, randomLocations.Length)],
});
}
private void InitRegion_OnClicked(object? sender, EventArgs e)
{
var microsoftLocation = new Location(47.64232, -122.13684);
PinsMap.MoveToRegion(MapSpan.FromCenterAndRadius(microsoftLocation, Distance.FromKilometers(1)));
if (PinsMap.Pins.Any(x => x.Location == microsoftLocation))
{
return;
}
var microsoftPin = new Pin()
{
Address = "One Microsoft Way, Redmond, USA",
Label = "Microsoft Visitors Center",
Location = microsoftLocation,
};
microsoftPin.MarkerClicked += (s, a) =>
{
DisplayAlert("Marker", "OK", "OK");
};
microsoftPin.InfoWindowClicked += (s, a) =>
{
DisplayAlert("Info", "OK", "OK");
};
PinsMap.Pins.Add(microsoftPin);
}
}

Просмотреть файл

@ -0,0 +1,5 @@
namespace CommunityToolkit.Maui.Sample.ViewModels.Views;
public class MapsPinsViewModel : BaseViewModel
{
}

Просмотреть файл

@ -0,0 +1,5 @@
namespace CommunityToolkit.Maui.Sample.ViewModels.Views;
public class BasicMapsViewModel : BaseViewModel
{
}

Просмотреть файл

@ -21,6 +21,8 @@ public sealed class ViewsGalleryViewModel : BaseGalleryViewModel
SectionModel.Create<AvatarViewSizesViewModel>("AvatarView Sizes Page", Colors.Red, "A page demonstrating AvatarViews with various size options."),
SectionModel.Create<DrawingViewViewModel>("DrawingView", Colors.Red, "DrawingView provides a canvas for users to \"paint\" on the screen. The drawing can also be captured and displayed as an Image."),
SectionModel.Create<ExpanderViewModel>("Expander Page", Colors.Red, "Expander allows collapse and expand content."),
SectionModel.Create<BasicMapsViewModel>("Windows Maps Basic Page", Colors.Red, "A page demonstrating a basic example of .NET MAUI Maps for Windows."),
SectionModel.Create<MapsPinsViewModel>("Windows Maps Pins Page", Colors.Red, "A page demonstrating .NET MAUI Maps for Windows with Pins."),
SectionModel.Create<LazyViewViewModel>("LazyView", Colors.Red, "LazyView is a view that allows you to load its children in a delayed manner."),
SectionModel.Create<MediaElementViewModel>("MediaElement", Colors.Red, "MediaElement is a view for playing video and audio"),
SectionModel.Create<MultiplePopupViewModel>("Mutiple Popups Page", Colors.Red, "A page demonstrating multiple different Popups"),

Просмотреть файл

@ -0,0 +1,27 @@
namespace CommunityToolkit.Maui.Maps;
/// <summary>
/// Maps Extensions for <see cref="MauiAppBuilder"/>
/// </summary>
public static class AppHostBuilderExtensions
{
/// <summary>
/// Initializes the .NET MAUI Community Toolkit Maps Library
/// </summary>
/// <param name="builder"><see cref="MauiAppBuilder"/> generated by <see cref="MauiApp"/> </param>
/// <param name="key"></param>
/// <returns><see cref="MauiAppBuilder"/></returns>
public static MauiAppBuilder UseMauiCommunityToolkitMaps(this MauiAppBuilder builder, string key)
{
builder.UseMauiMaps();
builder.ConfigureMauiHandlers(handlers =>
{
#if WINDOWS
CommunityToolkit.Maui.Maps.Handlers.MapHandlerWindows.MapsKey = key;
handlers.AddHandler<Microsoft.Maui.Controls.Maps.Map, CommunityToolkit.Maui.Maps.Handlers.MapHandlerWindows>();
#endif
});
return builder;
}
}

Просмотреть файл

@ -0,0 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
<TargetFrameworks Condition="'$(IncludeTizenTargetFrameworks)' == 'true'">$(TargetFrameworks);net7.0-tizen</TargetFrameworks>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup>
<GitInfoReportImportance>high</GitInfoReportImportance>
<PackageId>CommunityToolkit.Maui.Maps</PackageId>
<Summary>Community-created toolkit for .NET MAUI Maps</Summary>
<Authors>Microsoft</Authors>
<Owners>Microsoft</Owners>
<NeutralLanguage>en</NeutralLanguage>
<Product>CommunityToolkit.Maui (net7.0)</Product>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/CommunityToolkit/Maui</PackageProjectUrl>
<RepositoryUrl>https://github.com/CommunityToolkit/Maui</RepositoryUrl>
<PackageReleaseNotes>TBD</PackageReleaseNotes>
<DefineConstants>$(DefineConstants);</DefineConstants>
<UseFullSemVerForNuGet>false</UseFullSemVerForNuGet>
<Title>CommunityToolkit.Maui.Maps</Title>
<Description>The .NET MAUI Community Maps Toolkit is a collection of Maps</Description>
<PackageIcon>icon.png</PackageIcon>
<Product>$(AssemblyName) ($(TargetFramework))</Product>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<AssemblyFileVersion>1.0.0.0</AssemblyFileVersion>
<Version>1.0.0-pre1</Version>
<PackageVersion>$(Version)$(VersionSuffix)</PackageVersion>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageTags>dotnet,maui,toolkit,kit,communitytoolkit,dotnetcommunitytoolkit,maps</PackageTags>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\build\nuget.png" PackagePath="icon.png" Pack="true" />
<None Include="ReadMe.txt" pack="true" PackagePath="." />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls.Maps" Version="7.0.86" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,358 @@
using Microsoft.Maui.Maps.Handlers;
using Microsoft.Maui.Maps;
using Microsoft.Maui.Platform;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml;
using IMap = Microsoft.Maui.Maps.IMap;
using Windows.Devices.Geolocation;
using System.Text.Json;
using Microsoft.Maui.Controls.Maps;
namespace CommunityToolkit.Maui.Maps.Handlers;
/// <inheritdoc />
public partial class MapHandlerWindows : MapHandler
{
internal static string? MapsKey;
/// <summary>
/// Initializes a new instance of the <see cref="MapHandlerWindows"/> class.
/// </summary>
public MapHandlerWindows() : base(Mapper, CommandMapper)
{
Mapper.ModifyMapping(nameof(IMap.MapType), (handler, map, _) => MapMapType(handler, map));
Mapper.ModifyMapping(nameof(IMap.IsShowingUser), (handler, map, _) => MapIsShowingUser(handler, map));
Mapper.ModifyMapping(nameof(IMap.IsScrollEnabled), (handler, map, _) => MapIsScrollEnabled(handler, map));
Mapper.ModifyMapping(nameof(IMap.IsTrafficEnabled), (handler, map, _) => MapIsTrafficEnabled(handler, map));
Mapper.ModifyMapping(nameof(IMap.IsZoomEnabled), (handler, map, _) => MapIsZoomEnabled(handler, map));
Mapper.ModifyMapping(nameof(IMap.Pins), (handler, map, _) => MapPins(handler, map));
Mapper.ModifyMapping(nameof(IMap.Elements), (handler, map, _) => MapElements(handler, map));
CommandMapper.ModifyMapping(nameof(IMap.MoveToRegion), (handler, map, args, _) => MapMoveToRegion(handler, map, args));
}
/// <inheritdoc/>
protected override FrameworkElement CreatePlatformView()
{
if (string.IsNullOrEmpty(MapsKey))
{
throw new InvalidOperationException("You need to specify a Bing Maps Key");
}
var mapPage = GetMapHtmlPage(MapsKey);
var webView = new MauiWebView();
webView.NavigationCompleted += WebViewNavigationCompleted;
webView.WebMessageReceived += WebViewWebMessageReceived;
webView.LoadHtml(mapPage, null);
return webView;
}
/// <inheritdoc />
protected override void DisconnectHandler(FrameworkElement platformView)
{
if (PlatformView is MauiWebView mauiWebView)
{
mauiWebView.NavigationCompleted -= WebViewNavigationCompleted;
mauiWebView.WebMessageReceived -= WebViewWebMessageReceived;
}
base.DisconnectHandler(platformView);
}
/// <summary>
/// Maps Map type
/// </summary>
public static new void MapMapType(IMapHandler handler, IMap map)
{
CallJSMethod(handler.PlatformView, $"setMapType('{map.MapType}');");
}
/// <summary>
/// Maps IsZoomEnabled
/// </summary>
public static new void MapIsZoomEnabled(IMapHandler handler, IMap map)
{
CallJSMethod(handler.PlatformView, $"disableMapZoom({(!map.IsZoomEnabled).ToString().ToLower()});");
}
/// <summary>
/// Maps IsScrollEnabled
/// </summary>
public static new void MapIsScrollEnabled(IMapHandler handler, IMap map)
{
CallJSMethod(handler.PlatformView, $"disablePanning({(!map.IsScrollEnabled).ToString().ToLower()});");
}
/// <summary>
/// Maps IsTrafficEnabled
/// </summary>
public static new void MapIsTrafficEnabled(IMapHandler handler, IMap map)
{
CallJSMethod(handler.PlatformView, $"disableTraffic({(!map.IsTrafficEnabled).ToString().ToLower()});");
}
/// <summary>
/// Maps IsShowingUser
/// </summary>
public static new async void MapIsShowingUser(IMapHandler handler, IMap map)
{
if (map.IsShowingUser)
{
var location = await GetCurrentLocation();
if (location != null)
{
CallJSMethod(handler.PlatformView, $"addLocationPin({location.Latitude},{location.Longitude});");
}
}
else
{
CallJSMethod(handler.PlatformView, "removeLocationPin();");
}
}
/// <summary>
/// Map Pins
/// </summary>
public static new void MapPins(IMapHandler handler, IMap map)
{
CallJSMethod(handler.PlatformView, "removeAllPins();");
foreach (var pin in map.Pins)
{
CallJSMethod(handler.PlatformView, $"addPin({pin.Location.Latitude},{pin.Location.Longitude},'{pin.Label}', '{pin.Address}');");
}
}
/// <summary>
/// Map Elements
/// </summary>
public static new void MapElements(IMapHandler handler, IMap map) { }
MapSpan? regionToGo;
/// <summary>
/// Maps MoveToRegion
/// </summary>
public static new void MapMoveToRegion(IMapHandler handler, IMap map, object? arg)
{
var newRegion = arg as MapSpan;
if (newRegion == null)
{
return;
}
if (handler is MapHandlerWindows mapHandler)
{
mapHandler.regionToGo = newRegion;
}
CallJSMethod(handler.PlatformView, $"setRegion({newRegion.Center.Latitude},{newRegion.Center.Longitude});");
}
static void CallJSMethod(FrameworkElement platformWebView, string script)
{
if (platformWebView is WebView2 webView2 && webView2.CoreWebView2 != null)
{
platformWebView.DispatcherQueue.TryEnqueue(async () => await webView2.ExecuteScriptAsync(script));
}
}
static string GetMapHtmlPage(string key)
{
var str = @$"<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv=""Content-Security-Policy"" content=""default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval' 'unsafe-inline' https://*.bing.com https://*.virtualearth.net; style-src 'self' 'unsafe-inline' https://*.bing.com https://*.virtualearth.net; media-src *"">
<meta name=""viewport"" content=""user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width"">
<script type='text/javascript' src='https://www.bing.com/api/maps/mapcontrol?key={key}'></script>";
str += @" <script type='text/javascript'>
var map;
var locationPin;
var trafficManager;
function loadMap() {
map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
disableBirdseye : true,
// disableZooming: true,
// disablePanning: true,
showScalebar: false,
showLocateMeButton: false,
showDashboard: false,
showTermsLink: false,
showTrafficButton: false
});
loadTrafficModule();
Microsoft.Maps.Events.addHandler(map, 'viewrendered', function () { var bounds = map.getBounds(); invokeHandlerAction(bounds); });
}
function loadTrafficModule()
{
Microsoft.Maps.loadModule('Microsoft.Maps.Traffic', function () {
trafficManager = new Microsoft.Maps.Traffic.TrafficManager(map);
});
}
function disableTraffic(disable)
{
if(disable)
trafficManager.hide();
else
trafficManager.show();
}
function disableMapZoom(disable)
{
map.setOptions({
disableZooming: disable,
});
}
function disablePanning(disable)
{
map.setOptions({
disablePanning: disable,
});
}
function setMapType(mauiMapType)
{
var mapTypeID = Microsoft.Maps.MapTypeId.road;
switch(mauiMapType) {
case 'Street':
mapTypeID = Microsoft.Maps.MapTypeId.road;
break;
case 'Satellite':
mapTypeID = Microsoft.Maps.MapTypeId.aerial;
break;
case 'Hybrid':
mapTypeID = Microsoft.Maps.MapTypeId.aerial;
break;
default:
mapTypeID = Microsoft.Maps.MapTypeId.road;
}
map.setView({
mapTypeId: mapTypeID
});
}
function setRegion(latitude, longitude)
{
map.setView({
center: new Microsoft.Maps.Location(latitude, longitude),
});
}
function addLocationPin(latitude, longitude)
{
var location = new Microsoft.Maps.Location(latitude, longitude);
locationPin = new Microsoft.Maps.Pushpin(location, null);
map.entities.push(locationPin);
map.setView({
center: location
});
}
function removeLocationPin()
{
if(locationPin != null)
{
map.entities.remove(locationPin);
locationPin = null;
}
}
function removeAllPins()
{
map.entities.clear();
locationPin = null;
}
function addPin(latitude, longitude, label, address)
{
var location = new Microsoft.Maps.Location(latitude, longitude);
var pin = new Microsoft.Maps.Pushpin(location, {
title: label,
subTitle: address
});
map.entities.push(pin);
Microsoft.Maps.Events.addHandler(pin, 'click', function (e)
{
var clickedPin = {
label: e.target.getTitle(),
address: e.target.getSubTitle(),
location : e.target.getLocation()
};
invokeHandlerAction(clickedPin);
});
}
function invokeHandlerAction(data)
{
window.chrome.webview.postMessage(data);
}
</script>
<style>
body, html{
padding:0;
margin:0;
}
</style>
</head>
<body onload='loadMap();'>
<div id=""myMap""></div>
</body>
</html>";
return str;
}
static async Task<Location?> GetCurrentLocation()
{
var geoLocator = new Geolocator();
var position = await geoLocator.GetGeopositionAsync();
return new Location(position.Coordinate.Latitude, position.Coordinate.Longitude);
}
void WebViewNavigationCompleted(WebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs args)
{
// Update initial properties when our page is loaded
Mapper.UpdateProperties(this, VirtualView);
if (regionToGo != null)
{
MapMoveToRegion(this, VirtualView, regionToGo);
}
}
void WebViewWebMessageReceived(WebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2WebMessageReceivedEventArgs args)
{
var options = new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
};
var clickedPin = JsonSerializer.Deserialize<Pin>(args.WebMessageAsJson, options);
if (clickedPin?.Location != null)
{
clickedPin.SendMarkerClick();
clickedPin.SendInfoWindowClick();
return;
}
var mapRect = JsonSerializer.Deserialize<Bounds>(args.WebMessageAsJson, options);
if (mapRect?.Center != null)
{
VirtualView.VisibleRegion = new MapSpan(new Location(mapRect.Center?.Latitude ?? 0, mapRect.Center?.Longitude ?? 0), mapRect.Height, mapRect.Width);
}
}
class Center
{
public double Latitude { get; set; }
public double Longitude { get; set; }
public int Altitude { get; set; }
public int AltitudeReference { get; set; }
}
class Bounds
{
public Center? Center { get; set; }
public double Width { get; set; }
public double Height { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,45 @@
.NET MAUI Community Toolkit
## Initializing
In order to use the .NET MAUI Community Toolkit Maps you need to call the extension method in your `MauiProgram.cs` file as follows:
```csharp
using CommunityToolkit.Maui;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
// Initialize the .NET MAUI Community Toolkit Maps by adding the below line of code
.UseMauiCommunityToolkitMaps("key")
// After initializing the .NET MAUI Community Toolkit, optionally add additional fonts
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Continue initializing your .NET MAUI App here
return builder.Build();
}
}
```
## XAML usage
In order to make use of the toolkit within XAML you can use this namespace:
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
## Further information
For more information please visit:
- Our documentation site: https://docs.microsoft.com/dotnet/communitytoolkit/maui
- Our GitHub repository: https://github.com/CommunityToolkit/Maui

Просмотреть файл

@ -28,6 +28,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{60926304
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui.Analyzers.UnitTests", "CommunityToolkit.Maui.Analyzers.UnitTests\CommunityToolkit.Maui.Analyzers.UnitTests.csproj", "{36ED21C6-EAA1-4472-B9DC-C0DC5EF7CBBF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Maui.Maps", "CommunityToolkit.Maui.Maps\CommunityToolkit.Maui.Maps.csproj", "{C567B5C3-FE10-42D0-9FB8-3C2D9B0FBF63}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Maui.MediaElement", "CommunityToolkit.Maui.MediaElement\CommunityToolkit.Maui.MediaElement.csproj", "{D6FB00E5-B2BE-4F02-857B-985E991653CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Maui.MediaElement.Analyzers", "CommunityToolkit.Maui.MediaElement.Analyzers\CommunityToolkit.Maui.MediaElement.Analyzers.csproj", "{673856B7-78A5-4E61-AEBD-CBBC1A15B0D1}"
@ -68,6 +70,12 @@ Global
{36ED21C6-EAA1-4472-B9DC-C0DC5EF7CBBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{36ED21C6-EAA1-4472-B9DC-C0DC5EF7CBBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{36ED21C6-EAA1-4472-B9DC-C0DC5EF7CBBF}.Release|Any CPU.Build.0 = Release|Any CPU
{C567B5C3-FE10-42D0-9FB8-3C2D9B0FBF63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C567B5C3-FE10-42D0-9FB8-3C2D9B0FBF63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C567B5C3-FE10-42D0-9FB8-3C2D9B0FBF63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C567B5C3-FE10-42D0-9FB8-3C2D9B0FBF63}.Release|Any CPU.Build.0 = Release|Any CPU
{D6FB00E5-B2BE-4F02-857B-985E991653CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6FB00E5-B2BE-4F02-857B-985E991653CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6FB00E5-B2BE-4F02-857B-985E991653CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -80,6 +88,7 @@ Global
{DC82CAA2-90F9-4BA5-98F4-2232660DECF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC82CAA2-90F9-4BA5-98F4-2232660DECF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC82CAA2-90F9-4BA5-98F4-2232660DECF1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE