Skip to content

Commit

Permalink
Adds a GeoView controller to help with common MVVM challenges (#528)
Browse files Browse the repository at this point in the history
* Adds a GeoView controller to help with common MVVM challenges
  • Loading branch information
dotMorten authored Nov 8, 2023
1 parent 6bd4e83 commit 7c0b0b7
Show file tree
Hide file tree
Showing 16 changed files with 542 additions and 5 deletions.
5 changes: 3 additions & 2 deletions src/Samples/Toolkit.SampleApp.Maui/MauiProgram.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Esri.ArcGISRuntime.Maui;
using CommunityToolkit.Maui;
using Esri.ArcGISRuntime.Maui;
using Esri.ArcGISRuntime.Toolkit.Maui;

namespace Toolkit.SampleApp.Maui;
Expand All @@ -14,7 +15,7 @@ public static MauiApp CreateMauiApp()
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
}).UseArcGISRuntime().UseArcGISToolkit();
}).UseArcGISRuntime().UseArcGISToolkit().UseMauiCommunityToolkit();

return builder.Build();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Samples/Toolkit.SampleApp.Maui/SampleDatasource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public Sample(Type page)
if (string.IsNullOrEmpty(Name))
{
//Deduce name from type name
Name = Regex.Replace(Page.Name, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0").Replace("Arc GIS", "ArcGIS");
Name = Regex.Replace(Page.Name, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0").Replace("Arc GIS", "ArcGIS").Replace("Geo View", "GeoView");
if (Name.EndsWith("Sample"))
Name = Name.Substring(0, Name.Length - 6);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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="Toolkit.SampleApp.Maui.Samples.GeoViewControllerSample"
xmlns:local="clr-namespace:Toolkit.SampleApp.Maui.Samples"
xmlns:esri="clr-namespace:Esri.ArcGISRuntime.Maui;assembly=Esri.ArcGISRuntime.Maui"
xmlns:toolkit="clr-namespace:Esri.ArcGISRuntime.Toolkit.Maui;assembly=Esri.ArcGISRuntime.Toolkit.Maui"
xmlns:toolkitbase="clr-namespace:Esri.ArcGISRuntime.Toolkit;assembly=Esri.ArcGISRuntime.Toolkit.Maui"
xmlns:mauitoolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
Title="GeoViewController">
<ContentPage.Resources>
<local:GeoViewControllerSampleVM x:Key="VM" />
</ContentPage.Resources>
<Grid>
<esri:MapView x:Name="MyMapView"
Map="{Binding Map, Source={StaticResource VM}}"
toolkit:GeoViewController.GeoViewController="{Binding Controller, Source={StaticResource VM}}">
<esri:MapView.Behaviors>
<mauitoolkit:EventToCommandBehavior EventName="GeoViewTapped"
x:TypeArguments="esri:GeoViewInputEventArgs"
Command="{Binding GeoViewTappedCommand, Source={StaticResource VM}}" />
</esri:MapView.Behaviors>
</esri:MapView>
</Grid>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using CommunityToolkit.Mvvm.Input;
using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Maui;
using Esri.ArcGISRuntime.Toolkit.Maui;

namespace Toolkit.SampleApp.Maui.Samples;

public partial class GeoViewControllerSample : ContentPage
{
public GeoViewControllerSample()
{
InitializeComponent();
}
}

public partial class GeoViewControllerSampleVM
{
public Map Map { get; } = new Map(new Uri("https://www.arcgis.com/home/item.html?id=9f3a674e998f461580006e626611f9ad"));

public GeoViewController Controller { get; } = new GeoViewController();

[RelayCommand]
public async Task OnGeoViewTapped(GeoViewInputEventArgs eventArgs) => await Identify(eventArgs.Position, eventArgs.Location);

public async Task Identify(Point location, MapPoint? mapLocation)
{
Controller.DismissCallout();
var result = await Controller.IdentifyLayersAsync(location, 10);
if (result.FirstOrDefault()?.GeoElements?.FirstOrDefault() is GeoElement element)
{
Controller.ShowCalloutForGeoElement(element, location, new Esri.ArcGISRuntime.UI.CalloutDefinition(element));
}
else if (mapLocation is not null)
{
Controller.ShowCalloutAt(mapLocation, new Esri.ArcGISRuntime.UI.CalloutDefinition("No features found"));
}
}
}
11 changes: 11 additions & 0 deletions src/Samples/Toolkit.SampleApp.Maui/Toolkit.SampleApp.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@
<MauiXaml Remove="Samples\TimeSliderSample.xaml" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="6.0.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
</ItemGroup>

<ItemGroup>
<MauiXaml Update="Samples\GeoViewControllerSample.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>


<Choose>
<When Condition="'$(UseNugetPackage)'==''">
Expand Down
2 changes: 1 addition & 1 deletion src/Samples/Toolkit.SampleApp.UWP/SampleDatasource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ where t.GetTypeInfo().IsSubclassOf(typeof(Page)) && t.FullName.Contains(".Sample
}
if (string.IsNullOrEmpty(sample.Name))
{
sample.Name = Regex.Replace(sample.Page.Name, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0").Replace("Arc GIS", "ArcGIS");
sample.Name = Regex.Replace(sample.Page.Name, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0").Replace("Arc GIS", "ArcGIS").Replace("Geo View", "GeoView").Replace("Geo View", "GeoView");
if (sample.Name.EndsWith("Sample"))
sample.Name = sample.Name.Substring(0, sample.Name.Length - 6);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Page
x:Class="Esri.ArcGISRuntime.Toolkit.SampleApp.Samples.GeoViewController.GeoViewControllerSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Esri.ArcGISRuntime.Toolkit.SampleApp.Samples.GeoViewController"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:esri="using:Esri.ArcGISRuntime.UI.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkit="using:Esri.ArcGISRuntime.Toolkit.UI.Controls"
xmlns:toolkitui="using:Esri.ArcGISRuntime.Toolkit.UI"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid>
<esri:MapView x:Name="MyMapView" Map="{x:Bind VM.Map}"
toolkitui:GeoViewController.GeoViewController="{x:Bind VM.Controller}"
GeoViewTapped="{x:Bind VM.OnGeoViewTapped}" />
</Grid>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Esri.ArcGISRuntime.Toolkit.SampleApp.Samples.GeoViewController
{
public sealed partial class GeoViewControllerSample : Page
{
public GeoViewControllerSample()
{
this.InitializeComponent();
}
public GeoViewControllerSampleVM VM { get; } = new GeoViewControllerSampleVM();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Linq;
using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.UI.Controls;
using Windows.Foundation;

namespace Esri.ArcGISRuntime.Toolkit.SampleApp.Samples.GeoViewController;

public class GeoViewControllerSampleVM
{
public Map Map { get; } = new Map(new Uri("https://www.arcgis.com/home/item.html?id=9f3a674e998f461580006e626611f9ad"));

public UI.GeoViewController Controller { get; } = new UI.GeoViewController();

public void OnGeoViewTapped(object sender, GeoViewInputEventArgs eventArgs) => Identify(eventArgs.Position, eventArgs.Location);

public async void Identify(Point location, MapPoint mapLocation)
{
Controller.DismissCallout();
var result = await Controller.IdentifyLayersAsync(location, 10);
if (result.FirstOrDefault()?.GeoElements?.FirstOrDefault() is GeoElement element)
{
Controller.ShowCalloutForGeoElement(element, location, new Esri.ArcGISRuntime.UI.CalloutDefinition(element));
}
else if (mapLocation is not null)
{
Controller.ShowCalloutAt(mapLocation, new Esri.ArcGISRuntime.UI.CalloutDefinition("No features found"));
}
}
}
8 changes: 8 additions & 0 deletions src/Samples/Toolkit.SampleApp.UWP/Toolkit.Samples.UWP.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@
<Compile Include="Samples\FloorFilter\FloorFilterSample.xaml.cs">
<DependentUpon>FloorFilterSample.xaml</DependentUpon>
</Compile>
<Compile Include="Samples\GeoViewController\GeoViewControllerSample.xaml.cs">
<DependentUpon>GeoViewControllerSample.xaml</DependentUpon>
</Compile>
<Compile Include="Samples\GeoViewController\GeoViewControllerSampleVM.cs" />
<Compile Include="Samples\TimeSlider\TimeSliderSample.xaml.cs">
<DependentUpon>TimeSliderSample.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -227,6 +231,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Samples\GeoViewController\GeoViewControllerSample.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Samples\TimeSlider\TimeSliderSample.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
Expand Down
2 changes: 1 addition & 1 deletion src/Samples/Toolkit.SampleApp.WPF/SampleDatasource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ where t.GetTypeInfo().IsSubclassOf(typeof(UserControl)) && t.FullName.Contains("
sample.Name = sample.Page.Name;
if (sample.Name.EndsWith("Sample"))
sample.Name = sample.Name.Substring(0, sample.Name.Length - 6);
sample.Name = Regex.Replace(sample.Name, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0").Replace("Arc GIS", "ArcGIS");
sample.Name = Regex.Replace(sample.Name, @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0").Replace("Arc GIS", "ArcGIS").Replace("Geo View", "GeoView");
}
if (string.IsNullOrEmpty(sample.Category))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<UserControl x:Class="Esri.ArcGISRuntime.Toolkit.Samples.GeoViewController.GeoViewControllerSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Esri.ArcGISRuntime.Toolkit.Samples.GeoViewController"
xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
xmlns:Behaviors="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<local:GeoViewControllerSampleVM x:Key="VM" />
</UserControl.Resources>
<Grid>
<esri:MapView x:Name="MyMapView"
Map="{Binding Map, Source={StaticResource VM}}"
esri:GeoViewController.GeoViewController="{Binding Controller, Source={StaticResource VM}}">
<Behaviors:Interaction.Triggers>
<Behaviors:EventTrigger EventName="GeoViewTapped" >
<Behaviors:InvokeCommandAction Command="{Binding GeoViewTappedCommand, Source={StaticResource VM}}" PassEventArgsToCommand="True" />
</Behaviors:EventTrigger>
</Behaviors:Interaction.Triggers>
</esri:MapView>
</Grid>
</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Windows.Controls;

namespace Esri.ArcGISRuntime.Toolkit.Samples.GeoViewController
{
public partial class GeoViewControllerSample : UserControl
{
public GeoViewControllerSample()
{
InitializeComponent();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.Input;
using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.UI.Controls;

namespace Esri.ArcGISRuntime.Toolkit.Samples.GeoViewController;
public partial class GeoViewControllerSampleVM
{
public Map Map { get; } = new Map(new Uri("https://www.arcgis.com/home/item.html?id=9f3a674e998f461580006e626611f9ad"));

public IMyMapViewController Controller { get; } = new MyMapViewController();

[RelayCommand]
public async Task OnGeoViewTapped(GeoViewInputEventArgs eventArgs) => await Identify(eventArgs.Position, eventArgs.Location);

public async Task Identify(Point location, MapPoint? mapLocation)
{
Controller.DismissCallout();
var result = await Controller.IdentifyLayersAsync(location, 10);
if (result.FirstOrDefault()?.GeoElements?.FirstOrDefault() is GeoElement element)
{
Controller.ShowCalloutForGeoElement(element, location, new Esri.ArcGISRuntime.UI.CalloutDefinition(element));
_ = Controller.PanToAsync(mapLocation);
}
else if (mapLocation is not null)
{
Controller.ShowCalloutAt(mapLocation, new Esri.ArcGISRuntime.UI.CalloutDefinition("No features found"));
}
}
}

// Custom controller that extends the toolkit controller
public class MyMapViewController : UI.GeoViewController, IMyMapViewController
{
public MapView? ConnectedMapView => ConnectedView as MapView;

public Task PanToAsync(MapPoint? center)
{
if (center is null)
return Task.FromResult(false);
return ConnectedMapView?.SetViewpointCenterAsync(center) ?? Task.FromResult(false);
}

public MapPoint? ScreenToLocation(Point screenLocation) => ConnectedMapView?.ScreenToLocation(screenLocation);
}

// Custom interface for testability of VM
public interface IMyMapViewController
{
void DismissCallout();
void ShowCalloutForGeoElement(GeoElement element, Point tapPosition, ArcGISRuntime.UI.CalloutDefinition definition);
void ShowCalloutAt(MapPoint location, ArcGISRuntime.UI.CalloutDefinition definition);
Task<IReadOnlyList<IdentifyLayerResult>> IdentifyLayersAsync(Point screenPoint, double tolerance, bool returnPopupsOnly = false, CancellationToken cancellationToken = default);
MapPoint? ScreenToLocation(Point screenLocation);
Task PanToAsync(MapPoint? center);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
</ItemGroup>

<Choose>
<When Condition="'$(UseNugetPackage)'==''">
<ItemGroup>
Expand Down
Loading

0 comments on commit 7c0b0b7

Please sign in to comment.