{"id":2446352,"date":"2024-07-26T03:17:50","date_gmt":"2024-07-26T10:17:50","guid":{"rendered":"https:\/\/www.esri.com\/arcgis-blog\/?post_type=blog&#038;p=2446352"},"modified":"2024-08-12T02:34:03","modified_gmt":"2024-08-12T09:34:03","slug":"using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern","status":"publish","type":"blog","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern","title":{"rendered":"Using ArcGIS Maps SDK for .NET in an MVVM pattern"},"author":359632,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","format":"standard","meta":{"_acf_changed":false,"_searchwp_excluded":""},"categories":[738191],"tags":[772172,768202,775992,773362],"industry":[],"product":[761642,769142,36601],"class_list":["post-2446352","blog","type-blog","status-publish","format-standard","hentry","category-developers","tag-arcgis-developers","tag-arcgis-maps-sdks-for-native-apps","tag-mvvm","tag-native-development","product-platform","product-sdk-net","product-developers"],"acf":{"authors":[{"ID":359632,"user_firstname":"Hamish","user_lastname":"Duff","nickname":"Hamish Duff","user_nicename":"hduff","display_name":"Hamish Duff","user_email":"hduff@esri.com","user_url":"","user_registered":"2024-07-26 10:03:08","user_description":"I'm a product engineer on the ArcGIS Maps SDKs for Native Apps team.","user_avatar":"<img data-del=\"avatar\" src='https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/99325835-213x200.jpeg' class='avatar pp-user-avatar avatar-96 photo ' height='96' width='96'\/>"}],"short_description":"This blog will demonstrate how to implement ArcGIS Maps SDK for .NET in a Model-View-ViewModel (MVVM) pattern","flexible_content":[{"acf_fc_layout":"content","content":"<p style=\"text-align: left\">.NET applications commonly use the Model-View-ViewModel (MVVM) design pattern, in which code can be broken up into classes with a small number of well defined responsibilities. This can help improve code understandability, maintenance and ease of modification in larger code bases.<\/p>\n<h2 style=\"text-align: left\">What is MVVM?<\/h2>\n<p style=\"text-align: left\">Model-View-ViewModel (MVVM) is a pattern that separates application business logic from the user interface (UI). In short, the view describes the user interface (UI) and interacts with the view model through data bindings and commands, and it updates the model state. This helps keep the model separated from the view, which means changes do not need to be reflected in the view and vice versa.<\/p>\n<h2 style=\"text-align: left\">MVVM in an ArcGIS Maps SDK for .NET application<\/h2>\n<p style=\"text-align: left\">To demonstrate how to implement the MVVM pattern in a .NET Maps SDK app this blog post looks at the following use cases:<\/p>\n<ol style=\"text-align: left\">\n<li><a href=\"#create_a_.net_application\">Create a .NET application<\/a><\/li>\n<li><a href=\"#add_nuget_packages\">Add NuGet packages<\/a><\/li>\n<li><a href=\"#setup_dependency_injection\">Setup dependency injection<\/a><\/li>\n<li><a href=\"#display_data_on_a_mapview\">Display data on a MapView<\/a><\/li>\n<li><a href=\"#add_geoviewcontroller_to_mapview\">Add a GeoViewController to a MapView<\/a><\/li>\n<li><a href=\"#implement_layer_identification\">Implement layer identification<\/a><\/li>\n<li><a href=\"#change_viewpoint_from_view_model\">Change viewpoint from the view model<\/a><\/li>\n<\/ol>\n<p style=\"text-align: left\"><em>If you haven\u2019t installed the .NET Maps SDK already, you can do so by visiting the <a href=\"https:\/\/developers.arcgis.com\/net\/install-and-set-up\/\">install and set up pages<\/a> on the Esri Developer\u2019s website.<\/em><\/p>\n<p style=\"text-align: left\">In the following sections I describe how to build an <a href=\"https:\/\/github.com\/duffh\/arcgis-mvvm-demo\">ArcGIS Maps SDK for .NET app<\/a> that follows an MVVM pattern. As I am focusing on MVVM in this blog post I will highlight the areas of the application that are relevant to MVVM, where this is not the case I will provide a brief overview and link to more information on the topic.<\/p>\n<h2 id=\"create_a_.net_application\" style=\"text-align: left\">Create a .NET application<\/h2>\n<p style=\"text-align: left\">You can easily get started building an MVVM app by downloading the ArcGIS Maps SDK for .NET <a href=\"https:\/\/developers.arcgis.com\/net\/install-and-set-up\/#install-the-visual-studio-project-templates-optional\">template for .NET<\/a> from the\u00a0<a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=Esri.ArcGISRuntimeTemplates\">Microsoft Visual Studio marketplace<\/a>. This template references the appropriate NuGet packages for each platform, using the MVVM design pattern. In this blog I will be using .NET MAUI but the same patterns and practices apply to WPF, WinUI and UWP.<\/p>\n<p style=\"text-align: left\">The template provides boiler-plate code with a view model called <code>MapViewModel.cs<\/code> which is bound to the view in <code>MainPage.xaml.cs<\/code>.<\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">partial<\/span> <span style=\"color: #0000ff\">class<\/span> <span style=\"color: #2b91af\">MainPage<\/span> : ContentPage\r\n{\r\n    <span style=\"color: #0000ff\">public<\/span> MainPage()\r\n    {\r\n\tInitializeComponent();\r\n\r\n\t<span style=\"color: #0000ff\">this<\/span>.BindingContext = <span style=\"color: #0000ff\">new<\/span> MapViewModel();\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">Note that there are multiple ways to bind the view model to the view. In the template, <code>BindingContext<\/code> is defined in <code>MainPage.xaml.cs<\/code> code-behind but it could also be set in the <code>MainPage.xaml<\/code> file via xaml, as shown below.<\/p>\n<p style=\"text-align: left\"><!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">&lt;ContentPage.Resources&gt;\r\n   &lt;local:MapViewModel x:Key=<span style=\"color: #a31515\">\"VM\"<\/span> \/&gt;\r\n&lt;\/ContentPage.Resources&gt;\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">ArcGIS location services, data hosting services, and content management services require authentication with an API key or by authenticating with ArcGIS Online. You can learn more about API keys and how to generate them from the <a href=\"https:\/\/developers.arcgis.com\/documentation\/security-and-authentication\/api-key-authentication\/tutorials\/create-an-api-key\/\">Create an API key<\/a> tutorial. Once you have it, this API key can be added in the <code>MauiProgram.cs<\/code> file, and if you\u2019re using a project from the template, you can replace the placeholder text with the key for your app.<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">.UseApiKey(<span style=\"color: #a31515\">\"YOUR_API_KEY\"<\/span>)\r\n<\/pre>\n<\/div>\n<h2 id=\"add_nuget_packages\" style=\"text-align: left\">Add NuGet packages<\/h2>\n<p style=\"text-align: left\">To simplify the code and enhance the view model\u2019s capabilities, add the following package references to the project file.<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">&lt;PackageReference Include=<span style=\"color: #a31515\">\"CommunityToolkit.Maui\"<\/span> Version=<span style=\"color: #a31515\">\"8.0.1\"<\/span> \/&gt;\r\n&lt;PackageReference Include=<span style=\"color: #a31515\">\"CommunityToolkit.Mvvm\"<\/span> Version=<span style=\"color: #a31515\">\"8.2.2\"<\/span> \/&gt;\r\n&lt;PackageReference Include=<span style=\"color: #a31515\">\"Esri.ArcGISRuntime.Toolkit.Maui\"<\/span> Version=<span style=\"color: #a31515\">\"200.5.0\"<\/span> \/&gt;\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\"><!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<p>Include the <code>UseArcGISToolkit()<\/code> and <code>UseMauiCommunityToolkit()<\/code> statements in the host builder in <code>MauiProgram.cs<\/code>.<\/p>\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\"><span style=\"color: #2b91af\">var<\/span> builder = MauiApp.CreateBuilder();\r\nbuilder\r\n    .UseMauiApp&lt;App&gt;()\r\n    .ConfigureFonts(fonts =&gt;\r\n    {\r\n        fonts.AddFont(<span style=\"color: #a31515\">\"OpenSans-Regular.ttf\"<\/span>, <span style=\"color: #a31515\">\"OpenSansRegular\"<\/span>);\r\n        fonts.AddFont(<span style=\"color: #a31515\">\"OpenSans-Semibold.ttf\"<\/span>, <span style=\"color: #a31515\">\"OpenSansSemibold\"<\/span>);\r\n    })\r\n    .UseArcGISRuntime(config =&gt; config\r\n       .UseApiKey(<span style=\"color: #a31515\">\"YOUR_API_KEY\"<\/span>)\r\n       .ConfigureAuthentication(auth =&gt; auth\r\n           .UseDefaultChallengeHandler())\r\n      )\r\n    .UseArcGISToolkit()\r\n    .UseMauiCommunityToolkit();\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">Let\u2019s review the components used in the workflow described in this blog post.<\/p>\n<p style=\"text-align: left\"><strong>ArcGIS Maps SDK for .NET Toolkit (Esri.ArcGISRuntime.Toolkit.Maui) |<\/strong> <a href=\"https:\/\/www.nuget.org\/packages\/Esri.ArcGISRuntime.Toolkit.Maui\">NuGet<\/a><\/p>\n<p style=\"text-align: left\">The ArcGIS Maps SDK for .NET Toolkit is an open-source library of controls and components that can be used to improve your ease of development with the .NET Maps SDK. The toolkit repo can be found on <a href=\"https:\/\/github.com\/Esri\/arcgis-maps-sdk-dotnet-toolkit\">GitHub<\/a>.<\/p>\n<p style=\"text-align: left\">The <code>GeoViewController<\/code> in the toolkit provides a helper class which enables performing view operations such as layer identification and setting viewpoints on the <code>MapView<\/code> from the view model.<\/p>\n<p style=\"text-align: left\"><strong>.NET MAUI Community Toolkit (CommunityToolkit.Maui) |<\/strong> <a href=\"https:\/\/www.nuget.org\/packages\/CommunityToolkit.Maui\">NuGet<\/a><\/p>\n<p style=\"text-align: left\"><code>EventToCommandBehavior<\/code> is used to invoke a command on the bound view model using an event. Using this behavior, the view model can react to tap events on the <code>MapView<\/code>, change events on pickers, and loaded events on UI elements. This is used in this example application to handle tap events on clusters and features. Note that no toolkit is available to replicate this <code>EventToCommandBehavior<\/code> on WPF and WinUI but other solutions are possible.<\/p>\n<p style=\"text-align: left\"><strong>MVVM Toolkit (CommunityToolkit.Mvvm) |<\/strong> <a href=\"https:\/\/www.nuget.org\/packages\/CommunityToolkit.Mvvm\">NuGet<\/a><\/p>\n<p style=\"text-align: left\">This package simplifies the code in the view model by taking care of notify property changed boilerplate code through use of the provided <code>ObservableObject<\/code> type and simplified command binding using <code>[RelayCommand]<\/code>. Other MVVM libraries are available, and this is not a required package but I find it simplifies the code.<\/p>\n<h2 id=\"setup_dependency_injection\" style=\"text-align: left\">Setup dependency injection<\/h2>\n<p style=\"text-align: left\">The MVVM example app I am sharing in this blog post will utilize a single view model instance shared across one page that occurs in a single instance and another page that opens to display data based on current view model state.<\/p>\n<p style=\"text-align: left\">Add the following lines to <code>MauiProgram.cs<\/code> to register the single view model and page instances.<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">builder.Services.AddSingleton&lt;MainPage&gt;();\r\nbuilder.Services.AddSingleton&lt;MapViewModel&gt;();\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">The <code>MainPage.xaml.cs<\/code> constructor can then be updated as a dependency injection container will identify the dependency upon <code>MapViewModel.cs<\/code> when the object is created and handle the injection of the service.<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">partial<\/span> <span style=\"color: #0000ff\">class<\/span> <span style=\"color: #2b91af\">MainPage<\/span> : ContentPage\r\n{\r\n    <span style=\"color: #0000ff\">public<\/span> MainPage(MapViewModel mapViewModel)\r\n    {\r\n\tInitializeComponent();\r\n\r\n\t<span style=\"color: #0000ff\">this<\/span>.BindingContext = mapViewModel;\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">Using dependency injection provides some benefits. It removes the need for a class to locate its dependencies. Furthermore, dependencies can be mocked to improve testability.<\/p>\n"},{"acf_fc_layout":"content","content":"<h2 id=\"display_data_on_a_mapview\" style=\"text-align: left\">Display data on a MapView<\/h2>\n<p style=\"text-align: left\">Start by updating <code>MapViewModel.cs<\/code> to be a partial class inheriting from <code>ObservableObject<\/code> in the MVVM toolkit package. This gives access to <code>[RelayCommand]<\/code> and <code>[ObservableProperty]<\/code> attributes which are used to reduce code length and complexity. In this case you don&#8217;t need to directly implement <code>INotifyPropertyChanged<\/code>.<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">partial<\/span> <span style=\"color: #0000ff\">class<\/span> <span style=\"color: #2b91af\">MapViewModel<\/span> : ObservableObject\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">Then, remove the existing property changed code and update the <code>Map<\/code> property to use the <code>[ObservableProperty]<\/code> attribute.<\/p>\n<div>\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">[ObservableProperty]\r\n<span style=\"color: #0000ff\">private<\/span> Map? _map;\r\n<\/pre>\n<\/div>\n<h4 style=\"text-align: left\">Display a feature service using a feature layer<\/h4>\n<p style=\"text-align: left\">In the example app I use a <a href=\"https:\/\/services.arcgis.com\/V6ZHFr6zdgNZuVG0\/arcgis\/rest\/services\/Places_of_Worship_India\/FeatureServer\/0\">feature service<\/a> to display places of worship in India. To do this, instantiate the <code>Map<\/code> object in <code>MapViewModel.cs<\/code> with a basemap then create a feature layer using the feature service URL and add it to the maps operational layers. For more information on displaying a feature service see the <a href=\"https:\/\/developers.arcgis.com\/net\/maui\/sample-code\/feature-layer-feature-service\/\">Feature layer (feature service)<\/a> sample.<\/p>\n<h4 style=\"text-align: left\">Set up clustering data<\/h4>\n<p style=\"text-align: left\">To ensure the data from the feature layer displays as clusters when the map loads, clone the <code>UniqueValueRenderer<\/code> used by the feature layer to create a <code>ClusteringFeatureReduction<\/code> object. Then create a <code>SimpleLabelExpression<\/code> to display the total number of points in each cluster as a label. To see more in depth clustering content have a look at the <a href=\"https:\/\/developers.arcgis.com\/net\/maui\/sample-code\/display-clusters\/\">Display clusters<\/a> and <a href=\"https:\/\/developers.arcgis.com\/net\/maui\/sample-code\/configure-clusters\/\">Configure clusters<\/a> samples<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":2448832,"id":2448832,"title":"PlaceOfWorshipClusters","filename":"PlaceOfWorshipClusters-1.png","filesize":161790,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\/placeofworshipclusters-2","alt":"Picture of clustered data","author":"359632","description":"","caption":"Clustered place of worship data","name":"placeofworshipclusters-2","status":"inherit","uploaded_to":2446352,"date":"2024-07-31 12:28:03","modified":"2024-07-31 12:28:57","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1047,"height":553,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1.png","medium-width":464,"medium-height":245,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1.png","medium_large-width":768,"medium_large-height":406,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1.png","large-width":1047,"large-height":553,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1.png","1536x1536-width":1047,"1536x1536-height":553,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1.png","2048x2048-width":1047,"2048x2048-height":553,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1-826x436.png","card_image-width":826,"card_image-height":436,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PlaceOfWorshipClusters-1.png","wide_image-width":1047,"wide_image-height":553}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<h3 id=\"add_geoviewcontroller_to_mapview\" style=\"text-align: left\">Add a GeoViewController to a MapView<\/h3>\n<p style=\"text-align: left\">To make use of layer identification and setting viewpoints, add a <code>GeoViewController<\/code> to the view-model and a <code>GeoViewTappedCommand<\/code> to respond to tap events.<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">public<\/span> GeoViewController Controller { <span style=\"color: #0000ff\">get<\/span>; } = <span style=\"color: #0000ff\">new<\/span> GeoViewController();\r\n\r\n[RelayCommand]\r\n<span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">async<\/span> Task GeoViewTapped(GeoViewInputEventArgs eventArgs) { }\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">Note that using the MVVM Toolkit commands with the attribute <code>[RelayCommand]<\/code> are exposed with the word \u201cCommand\u201d appended to the end of the method signature. So, in this case the view will bind to <code>GeoViewTappedCommand<\/code>.<\/p>\n<p style=\"text-align: left\">Add the following namespaces to the view:<\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">xmlns:mauitoolkit=<span style=\"color: #a31515\">\"http:\/\/schemas.microsoft.com\/dotnet\/2022\/maui\/toolkit\"<\/span>\r\nxmlns:toolkit=<span style=\"color: #a31515\">\"clr-namespace:Esri.ArcGISRuntime.Toolkit.Maui;assembly=Esri.ArcGISRuntime.Toolkit.Maui\"<\/span>\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">Update the <code>MapView<\/code> to include the <code>GeoViewController<\/code> and an <code>EventToCommandBehavior<\/code> which allows commands to be invoked on the view model when a <code>GeoViewTapped<\/code> event occurs.<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">&lt;esri:MapView x:Name=<span style=\"color: #a31515\">\"MyMapView\"<\/span>\r\n              Grid.ColumnSpan=<span style=\"color: #a31515\">\"2\"<\/span>\r\n              toolkit:GeoViewController.GeoViewController=<span style=\"color: #a31515\">\"{Binding Controller}\"<\/span>\r\n              Map=<span style=\"color: #a31515\">\"{Binding Map}\"<\/span>&gt;\r\n    &lt;esri:MapView.Behaviors&gt;\r\n        &lt;mauitoolkit:EventToCommandBehavior x:TypeArguments=<span style=\"color: #a31515\">\"esri:GeoViewInputEventArgs\"<\/span>\r\n                                            Command=<span style=\"color: #a31515\">\"{Binding GeoViewTappedCommand}\"<\/span>\r\n                                            EventName=<span style=\"color: #a31515\">\"GeoViewTapped\"<\/span> \/&gt;\r\n    &lt;\/esri:MapView.Behaviors&gt;\r\n&lt;\/esri:MapView&gt;\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">In this case, the <code>Map<\/code> is bound to a <code>Map<\/code> object on the view model, the <code>GeoViewController<\/code> is bound to the <code>GeoViewController<\/code> and the <code>GeoViewTapped<\/code> event invokes the <code>GeoViewTappedCommand<\/code>, passing <code>GeoViewInputEventArgs<\/code> as a parameter.<\/p>\n<h3 id=\"implement_layer_identification\" style=\"text-align: left\">Implement layer identification<\/h3>\n<p style=\"text-align: left\">The <code>GeoViewController<\/code> is set up to invoke a <code>GeoViewTappedCommand<\/code> which means the view model can respond to tap events from the view.<\/p>\n<p style=\"text-align: left\">The main concept to understand when performing layer identification from the view model is to use the <code>GeoViewController<\/code>. Once you have a <code>GeoViewController<\/code> configured to react to a <code>GeoViewTapped<\/code> event you can update your code in <code>MapViewModel<\/code> as follows.<\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">[RelayCommand]\r\n<span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">async<\/span> Task GeoViewTapped(GeoViewInputEventArgs eventArgs) =&gt; <span style=\"color: #0000ff\">await<\/span> Identify(eventArgs.Position, eventArgs.Location);\r\n\r\n<span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">async<\/span> Task Identify(Point location, MapPoint? mapLocation)\r\n{\r\n        <span style=\"color: #008000\">\/\/ Identify the tapped observation.<\/span>\r\n        IdentifyLayerResult? results = <span style=\"color: #0000ff\">await<\/span> Controller.IdentifyLayerAsync(_featureLayer, location, 10, <span style=\"color: #0000ff\">false<\/span>);\r\n}<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n"},{"acf_fc_layout":"content","content":"<p style=\"text-align: left\">The following sections give an overview of the code used in the example app. To view the context of these code snippets, see the <a href=\"https:\/\/github.com\/duffh\/arcgis-mvvm-demo\">full code and try the app out yourself or clone the repo on GitHub<\/a>.<\/p>\n<p style=\"text-align: left\">I&#8217;ve added an additional view model to wrap the <code>GeoElement<\/code> objects. This keeps the <code>GeoElement<\/code> data model separate from the view and allows for the data to be validated or formatted prior to presentation.<\/p>\n<div>\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">public<\/span> PlaceOfWorshipViewModel(<span style=\"color: #2b91af\">int<\/span> index, ArcGISFeature feature)\r\n{\r\n    <span style=\"color: #008000\">\/\/ Set the default property values.<\/span>\r\n    Name = Religion = Fid = <span style=\"color: #a31515\">\"null\"<\/span>;\r\n\r\n    <span style=\"color: #008000\">\/\/ Set the display index and feature.<\/span>\r\n    ArcGISFeature = feature;\r\n    DisplayIndex = index;\r\n\r\n    <span style=\"color: #008000\">\/\/ Get attribute values from the attributes dictionary. <\/span>\r\n    <span style=\"color: #0000ff\">if<\/span> (feature.Attributes.TryGetValue(<span style=\"color: #a31515\">\"fid\"<\/span>, <span style=\"color: #0000ff\">out<\/span> <span style=\"color: #2b91af\">object?<\/span> fidValue) &amp;&amp; fidValue != <span style=\"color: #0000ff\">null<\/span> &amp;&amp; fidValue <span style=\"color: #0000ff\">is<\/span> <span style=\"color: #2b91af\">long<\/span> fid) Fid = fid.ToString();\r\n    <span style=\"color: #0000ff\">if<\/span> (feature.Attributes.TryGetValue(<span style=\"color: #a31515\">\"religion\"<\/span>, <span style=\"color: #0000ff\">out<\/span> <span style=\"color: #2b91af\">object?<\/span> religionValue) &amp;&amp; religionValue != <span style=\"color: #0000ff\">null<\/span>) Religion = (<span style=\"color: #2b91af\">string<\/span>)religionValue;\r\n    <span style=\"color: #0000ff\">if<\/span> (feature.Attributes.TryGetValue(<span style=\"color: #a31515\">\"name\"<\/span>, <span style=\"color: #0000ff\">out<\/span> <span style=\"color: #2b91af\">object?<\/span> nameValue) &amp;&amp; nameValue != <span style=\"color: #0000ff\">null<\/span>) Name = (<span style=\"color: #2b91af\">string<\/span>)nameValue; \r\n\r\n    <span style=\"color: #008000\">\/\/ Given the feature geometry get a formatted coordinate string.<\/span>\r\n    FormattedCoordinates = CoordinateFormatter.ToLatitudeLongitude((MapPoint)ArcGISFeature.Geometry!, LatitudeLongitudeFormat.DegreesDecimalMinutes, 2);\r\n}\r\n<\/pre>\n<\/div>\n<p style=\"text-align: left\">I then create <code>PlaceOfWorshipViewModel<\/code> objects by passing in an <code>ArcGISFeature<\/code> and populating properties used in the view. None of the properties on this view model change once presented so they do not need to be observable. Each feature\u2019s geometry is formatted using the <code>CoordinateFormatter<\/code> to present the information in a way that is easier to read in the view. The <code>DisplayIndex<\/code> field is added to the view model to display the item index in the view and improve readability when scrolling through large numbers of items.<\/p>\n<p style=\"text-align: left\">I have created another .NET MAUI view called <code>GeoElementSelectionPage<\/code>, this is used to display the data contained in a tapped cluster.<\/p>\n<p style=\"text-align: left\">A collection view on the <code>GeoElementSelectionPage<\/code>\u00a0is used to display an <code>ObservableCollection<\/code> of <code>PlaceOfWorshipViewModel<\/code> objects in the <code>MapViewModel<\/code>.<\/p>\n<p style=\"text-align: left\">As clusters in this data set can contain thousands of <code>GeoElement<\/code> objects, the number of items in the collection is incrementally increased as you scroll. This is achieved by adding a <code>LoadMore()<\/code> method in the <code>MapViewModel<\/code> which is called when the number of items in the collection hits a specified threshold.<\/p>\n<p style=\"text-align: left\">The core concept is to use the <code>GeoViewController<\/code> to perform identification in the view model &#8211; see the <a href=\"https:\/\/github.com\/duffh\/arcgis-mvvm-demo\">example app repo<\/a> for the full code that deals with the more complex workflow of identifying clustered data on a layer.<\/p>\n<div style=\"text-align: left\">\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\"><span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">async<\/span> Task Identify(Point location, MapPoint? mapLocation)\r\n{<\/pre>\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">    <span style=\"color: #008000\">\/\/ Identify the tapped observation.<\/span><\/pre>\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">    IdentifyLayerResult? results = <span style=\"color: #0000ff\">await<\/span> Controller.IdentifyLayerAsync(_featureLayer, location, 10, <span style=\"color: #0000ff\">false<\/span>); \r\n}<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<h4 style=\"text-align: left\">Flesh out the view and view model<\/h4>\n<p style=\"text-align: left\">Add more commands and properties with <code>[RelayCommand]<\/code> and <code>[ObservableProperty]<\/code> attributes to perform actions like navigating away from the <code>GeoElementSelectionPage<\/code> and hold the displayed <code>GeoElement<\/code> objects etc. Using properties in the <code>PlaceOfWorshipViewModel<\/code> and commands in the <code>MapViewModel<\/code> you can create a UI to display all of the elements within a tapped cluster and format their data for presentation.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":2448842,"id":2448842,"title":"GeoElementsInCluster","filename":"GeoElementsInCluster.png","filesize":78433,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\/geoelementsincluster","alt":"Picture of data contained within a cluster","author":"359632","description":"","caption":"GeoElements contained within a tapped cluster","name":"geoelementsincluster","status":"inherit","uploaded_to":2446352,"date":"2024-07-31 12:29:31","modified":"2024-07-31 12:30:12","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1047,"height":553,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster.png","medium-width":464,"medium-height":245,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster.png","medium_large-width":768,"medium_large-height":406,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster.png","large-width":1047,"large-height":553,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster.png","1536x1536-width":1047,"1536x1536-height":553,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster.png","2048x2048-width":1047,"2048x2048-height":553,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster-826x436.png","card_image-width":826,"card_image-height":436,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/GeoElementsInCluster.png","wide_image-width":1047,"wide_image-height":553}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<h3 id=\"change_viewpoint_from_view_model\">Change viewpoint from the view model<\/h3>\n<p>This section will outline how to update the viewpoint of the <code>MapView<\/code> from the <code>MapViewModel<\/code>.<\/p>\n<p>First lets update the viewpoint when a picker selection change event occurs.<\/p>\n<p>Add a picker to <code>MainPage<\/code> update the current viewpoint of the <code>MapView<\/code>.<\/p>\n<div>\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0 0 0 10px;line-height: 125%\">&lt;Picker x:Name=<span style=\"color: #a31515\">\"RegionPicker\"<\/span>\r\n        Title=<span style=\"color: #a31515\">\"Scope to region\"<\/span>\r\n        SelectedItem=<span style=\"color: #a31515\">\"{Binding SelectedRegion}\"<\/span>&gt;\r\n    &lt;Picker.Behaviors&gt;\r\n        &lt;mauitoolkit:EventToCommandBehavior Command=<span style=\"color: #a31515\">\"{Binding SelectedRegionChangedCommand}\"<\/span>\r\n                                            CommandParameter=<span style=\"color: #a31515\">\"{Binding Source={x:Reference RegionPicker}, Path=SelectedIndex}\"<\/span>\r\n                                            EventName=<span style=\"color: #a31515\">\"SelectedIndexChanged\"<\/span> \/&gt;\r\n    &lt;\/Picker.Behaviors&gt;\r\n    &lt;Picker.Items&gt;\r\n        &lt;x:String&gt;North&lt;\/x:String&gt;\r\n        &lt;x:String&gt;South&lt;\/x:String&gt;\r\n        &lt;x:String&gt;East&lt;\/x:String&gt;\r\n        &lt;x:String&gt;West&lt;\/x:String&gt;\r\n        &lt;x:String&gt;Central&lt;\/x:String&gt;\r\n    &lt;\/Picker.Items&gt;\r\n&lt;\/Picker&gt;<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Again, use the <code>EventToCommandBehavior<\/code> to invoke a command on the view model when the picker\u2019s selection changes.<\/p>\n<p style=\"text-align: justify\">In <code>MapViewModel<\/code>, implement a command to respond to the index changed event.<\/p>\n<div>\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0 0 0 10px;line-height: 125%\">[RelayCommand]\r\n<span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">async<\/span> Task SelectedRegionChanged(<span style=\"color: #2b91af\">int<\/span> selectedIndex)\r\n{\r\n    <span style=\"color: #008000\">\/\/ Get the full extent of the feature layer.<\/span>\r\n    Viewpoint viewpoint = <span style=\"color: #0000ff\">new<\/span> Viewpoint(_featureLayer!.FullExtent!);\r\n\r\n    <span style=\"color: #008000\">\/\/ Get the spatial reference of the feature layer.<\/span>\r\n    SpatialReference spatialReference = SpatialReference.Create(3857);\r\n\r\n    <span style=\"color: #2b91af\">double<\/span> scale = 10.5e6;\r\n\r\n    <span style=\"color: #0000ff\">switch<\/span> (selectedIndex)\r\n    {\r\n        <span style=\"color: #0000ff\">case<\/span> 0:\r\n            viewpoint = <span style=\"color: #0000ff\">new<\/span> Viewpoint(<span style=\"color: #0000ff\">new<\/span> MapPoint(8597888, 3622342, spatialReference), scale);\r\n            <span style=\"color: #0000ff\">break<\/span>;\r\n        <span style=\"color: #0000ff\">case<\/span> 1:\r\n            viewpoint = <span style=\"color: #0000ff\">new<\/span> Viewpoint(<span style=\"color: #0000ff\">new<\/span> MapPoint(8617655, 1603703, spatialReference), scale);\r\n            <span style=\"color: #0000ff\">break<\/span>;\r\n        <span style=\"color: #0000ff\">case<\/span> 2:\r\n            viewpoint = <span style=\"color: #0000ff\">new<\/span> Viewpoint(<span style=\"color: #0000ff\">new<\/span> MapPoint(9947837, 2859542, spatialReference), scale);\r\n            <span style=\"color: #0000ff\">break<\/span>;\r\n        <span style=\"color: #0000ff\">case<\/span> 3:\r\n            viewpoint = <span style=\"color: #0000ff\">new<\/span> Viewpoint(<span style=\"color: #0000ff\">new<\/span> MapPoint(7966035, 2803998, spatialReference), scale);\r\n            <span style=\"color: #0000ff\">break<\/span>;\r\n        <span style=\"color: #0000ff\">default<\/span>:\r\n            <span style=\"color: #0000ff\">break<\/span>;\r\n    }\r\n\r\n    <span style=\"color: #008000\">\/\/ Set the viewpoint based on the desired selection.<\/span>\r\n    <span style=\"color: #0000ff\">await<\/span> Controller.SetViewpointAsync(viewpoint);\r\n\r\n    <span style=\"color: #008000\">\/\/ Reset the selected region for the next navigation.<\/span>\r\n    SelectedRegion = <span style=\"color: #0000ff\">null<\/span>;\r\n}<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p style=\"text-align: justify\">Use the <code>GeoViewController<\/code> to update the viewpoint with <code>SetViewpointAsync()<\/code>, this will update the view from the view model.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":2448852,"id":2448852,"title":"PickerViewpointChange","filename":"PickerViewpointChange.png","filesize":159038,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\/pickerviewpointchange","alt":"Picture of picker to change viewpoint","author":"359632","description":"","caption":"Change viewpoint with picker","name":"pickerviewpointchange","status":"inherit","uploaded_to":2446352,"date":"2024-07-31 12:30:34","modified":"2024-07-31 12:30:58","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1047,"height":553,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange.png","medium-width":464,"medium-height":245,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange.png","medium_large-width":768,"medium_large-height":406,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange.png","large-width":1047,"large-height":553,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange.png","1536x1536-width":1047,"1536x1536-height":553,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange.png","2048x2048-width":1047,"2048x2048-height":553,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange-826x436.png","card_image-width":826,"card_image-height":436,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/PickerViewpointChange.png","wide_image-width":1047,"wide_image-height":553}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p style=\"text-align: left\">Now, lets respond to a changed selection in a collection view. We will update the viewpoint and show a callout using the <code>GeoViewController<\/code>.<\/p>\n<p style=\"text-align: left\">In the <code>GeoElementSelectionPage<\/code> the <code>GeoElementCollectionView.SelectionChangedCommand<\/code> can be bound to the <code>MapViewModel<\/code>.<\/p>\n<p style=\"text-align: left\">Add a new <code>SelectedGeoElementChanged()<\/code> method with the <code>[RelayCommand]<\/code> attribute. This will take the selected item as a parameter to be used to update the viewpoint.<\/p>\n<div>\n<pre style=\"background: #f5f5f5;overflow: auto;width: auto;border: solid purple;border-width: 0em 0em 0em .2em;padding: .2em .6em;font-size: 15px;margin: 0;line-height: 125%\">[RelayCommand]\r\n<span style=\"color: #0000ff\">public<\/span> <span style=\"color: #0000ff\">async<\/span> Task SelectedGeoElementChanged(PlaceOfWorshipViewModel selectedItem)\r\n{\r\n    <span style=\"color: #0000ff\">if<\/span> (selectedItem == <span style=\"color: #0000ff\">null<\/span>) <span style=\"color: #0000ff\">return<\/span>;\r\n    \r\n    <span style=\"color: #008000\">\/\/ Clear any currently selected features.<\/span>\r\n    _featureLayer?.ClearSelection();\r\n\r\n    <span style=\"color: #008000\">\/\/ Get the map point for the selected view model.<\/span>\r\n    MapPoint mapPoint = (MapPoint)selectedItem.ArcGISFeature.Geometry!;\r\n\r\n    <span style=\"color: #008000\">\/\/ Close the modal page.<\/span>\r\n    <span style=\"color: #0000ff\">await<\/span> Shell.Current.GoToAsync(<span style=\"color: #a31515\">\"..\"<\/span>);\r\n\r\n    <span style=\"color: #008000\">\/\/ Select the feature associated with the selected view model.<\/span>\r\n    _featureLayer?.SelectFeature(selectedItem.ArcGISFeature);\r\n\r\n    <span style=\"color: #008000\">\/\/ Navigate to the selected feature and show a callout.<\/span>\r\n    <span style=\"color: #008000\">\/\/ Scale is set to a value smaller than the layers feature reduction max scale so that clusters do not render when the navigation completes.<\/span>\r\n    <span style=\"color: #0000ff\">await<\/span> Controller.SetViewpointAsync(<span style=\"color: #0000ff\">new<\/span> Viewpoint(mapPoint, 1e4), TimeSpan.FromSeconds(1));\r\n\r\n    <span style=\"color: #008000\">\/\/ Use an arcade expression to fallback to a default value if the properties used in the callout are null.<\/span>\r\n    <span style=\"color: #2b91af\">var<\/span> calloutDefn = <span style=\"color: #0000ff\">new<\/span> Esri.ArcGISRuntime.UI.CalloutDefinition(selectedItem.ArcGISFeature, <span style=\"color: #a31515\">\"DefaultValue($feature.name, 'null')\"<\/span>, <span style=\"color: #a31515\">\"DefaultValue($feature.religion, 'null')\"<\/span>);\r\n    Controller.ShowCalloutForGeoElement(selectedItem.ArcGISFeature, <span style=\"color: #0000ff\">default<\/span>, calloutDefn);\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p style=\"text-align: left\">When a <code>GeoElement<\/code> is selected in the <code>CollectionView<\/code> the <code>GeoElementSelectionPage<\/code> closes and any selected items on the feature layer are cleared. The <code>GeoElement<\/code> is then selected on the feature layer and the viewpoint is updated to center on its geometry. A callout is displayed for the <code>GeoElement<\/code> to display its name using <code>GeoViewController.ShowCalloutForGeoElement()<\/code>.<\/p>\n<p style=\"text-align: left\">The point to highlight here is the use of the <code>GeoViewController.SetViewpointAsync()<\/code> as this is the key to performing viewpoint change operations on the view from the view model.<\/p>\n"},{"acf_fc_layout":"content","content":"<h2 style=\"text-align: left\">Finishing up<\/h2>\n<p style=\"text-align: left\">In the <a href=\"https:\/\/github.com\/duffh\/arcgis-mvvm-demo\">completed code<\/a> in the example app I have included a toggle to enable and disable clustering and a <a href=\"https:\/\/github.com\/Esri\/arcgis-maps-sdk-dotnet-toolkit\/blob\/main\/docs\/legend.md\">legend<\/a> to display the predominant religion in each cluster. These have been added following the processes outlined earlier in this blog post.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":2448862,"id":2448862,"title":"FullApplicationDemonstration","filename":"FullApplicationDemonstration.gif","filesize":1576142,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration.gif","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\/fullapplicationdemonstration","alt":"GIF of full application features","author":"359632","description":"","caption":"Completed application full feature demonstration","name":"fullapplicationdemonstration","status":"inherit","uploaded_to":2446352,"date":"2024-07-31 12:33:32","modified":"2024-07-31 12:34:13","menu_order":0,"mime_type":"image\/gif","type":"image","subtype":"gif","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1046,"height":554,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration-213x200.gif","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration.gif","medium-width":464,"medium-height":246,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration.gif","medium_large-width":768,"medium_large-height":407,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration.gif","large-width":1046,"large-height":554,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration.gif","1536x1536-width":1046,"1536x1536-height":554,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration.gif","2048x2048-width":1046,"2048x2048-height":554,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration-826x437.gif","card_image-width":826,"card_image-height":437,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/FullApplicationDemonstration.gif","wide_image-width":1046,"wide_image-height":554}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<h2 style=\"text-align: left\"><a name=\"_Toc172300014\"><\/a>Summary<\/h2>\n<p style=\"text-align: left\">Model-View-View-Model (MVVM) is widely used pattern in .NET applications. In this blog post I\u2019ve demonstrated how to get started with a <a href=\"https:\/\/github.com\/duffh\/arcgis-mvvm-demo\">simple MVVM application<\/a> using a mixture of helpful external packages.<\/p>\n<p style=\"text-align: left\">As an example, I\u2019ve shown how to bind a <code>Map<\/code> object in view model to a <code>MapView<\/code> in a view. I gave an overview of the usage of the <code>GeoViewController<\/code> and walked through implementing identification functionality in the view-model to respond to tap events in the view. I discussed how to configure the view model to update the <code>MapView<\/code> viewpoint and display callouts using the ArcGIS Maps SDK for .NET Toolkit <code>GeoViewController<\/code>.<\/p>\n<p>Let us know how you&#8217;re getting on with your MVVM apps in the <a href=\"https:\/\/community.esri.com\/t5\/net-maps-sdk-questions\/bd-p\/arcgis-runtime-sdk-dotnet-questions\">Esri Community forums<\/a>, and if you haven&#8217;t already, check out our developer documentation for more tips and guides on how to use the <a href=\"https:\/\/developers.arcgis.com\/net\/\">ArcGIS Maps SDK for .NET<\/a>.<\/p>\n<h3>Useful resources<\/h3>\n<p style=\"text-align: left\">The <a href=\"https:\/\/developers.arcgis.com\/net\/maui\/sample-code\/\">ArcGIS Maps SDK for .NET samples applications<\/a> showcase capabilities and functionality offered by ArcGIS Maps SDKs for Native Apps via simple workflows. These focused samples enable you to easily understand the concepts and allow you to copy code snippets directly into your application. To maintain that simplicity, the applications implement sample logic in the code-behind for each sample. This also makes it easier to copy the sample code into another application without additional steps. This blog post can be used in conjunction with the .NET samples applications to implement the broad range of functionality offered by the ArcGIS Maps SDK for .NET.<\/p>\n<ul>\n<li style=\"text-align: left\"><a href=\"https:\/\/github.com\/duffh\/arcgis-mvvm-demo\">MauiArcGISMVVM<\/a>: the project described in this article<\/li>\n<li style=\"text-align: left\">Display clusters Native Maps SDKs sample: <a href=\"https:\/\/developers.arcgis.com\/kotlin\/styles-and-data-visualization\/display-clusters\/\">Kotlin<\/a> | <a href=\"https:\/\/developers.arcgis.com\/net\/maui\/sample-code\/display-clusters\/\">.NET<\/a> | <a href=\"https:\/\/developers.arcgis.com\/qt\/styles-and-data-visualization\/display-clusters\/\">Qt<\/a> | <a href=\"https:\/\/developers.arcgis.com\/swift\/sample-code\/display-clusters\/\">Swift<\/a><\/li>\n<li style=\"text-align: left\"><a href=\"https:\/\/developers.arcgis.com\/net\/maui\/sample-code\/\">ArcGIS Maps SDK for .NET sample code<\/a><\/li>\n<li style=\"text-align: left\"><a href=\"https:\/\/developers.arcgis.com\/net\/toolkit\/\">ArcGIS Maps SDK for .NET Toolkit<\/a><\/li>\n<li><a href=\"https:\/\/developers.arcgis.com\/net\/api-reference\/api\/netwin\/Esri.ArcGISRuntime\/index.html\">ArcGIS Maps SDK for .NET API Reference<\/a><\/li>\n<li style=\"text-align: left\"><a href=\"https:\/\/developers.arcgis.com\/documentation\/mapping-and-location-services\/get-started\/\">Sign up for an ArcGIS Location Platform or ArcGIS Account<\/a><\/li>\n<\/ul>\n"}],"related_articles":[{"ID":2330452,"post_author":"357152","post_date":"2024-05-08 18:25:07","post_date_gmt":"2024-05-09 01:25:07","post_content":"","post_title":"It's about time: Explore new features for date and time management in the ArcGIS Maps SDK for .NET","post_excerpt":"","post_status":"publish","comment_status":"open","ping_status":"closed","post_password":"","post_name":"its-about-time-explore-new-features-for-date-and-time-management-in-the-native-maps-sdk-for-net","to_ping":"","pinged":"","post_modified":"2024-05-09 12:02:04","post_modified_gmt":"2024-05-09 19:02:04","post_content_filtered":"","post_parent":0,"guid":"https:\/\/www.esri.com\/arcgis-blog\/?post_type=blog&#038;p=2330452","menu_order":0,"post_type":"blog","post_mime_type":"","comment_count":"0","filter":"raw"}],"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/mvvm_maui_card.png","wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/mvvm_maui_banner.png"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.9 (Yoast SEO v25.9) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Using ArcGIS Maps SDK for .NET in an MVVM pattern<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Using ArcGIS Maps SDK for .NET in an MVVM pattern\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\" \/>\n<meta property=\"og:site_name\" content=\"ArcGIS Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/esrigis\/\" \/>\n<meta property=\"article:modified_time\" content=\"2024-08-12T09:34:03+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@ESRI\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":[\"Article\",\"BlogPosting\"],\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\"},\"author\":{\"name\":\"Hamish Duff\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/776665929a66fa840b76139acd87f394\"},\"headline\":\"Using ArcGIS Maps SDK for .NET in an MVVM pattern\",\"datePublished\":\"2024-07-26T10:17:50+00:00\",\"dateModified\":\"2024-08-12T09:34:03+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\"},\"wordCount\":10,\"publisher\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#organization\"},\"keywords\":[\"ArcGIS Developers\",\"ArcGIS Maps SDKs for Native Apps\",\"MVVM\",\"native development\"],\"articleSection\":[\"Developers\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\",\"name\":\"Using ArcGIS Maps SDK for .NET in an MVVM pattern\",\"isPartOf\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#website\"},\"datePublished\":\"2024-07-26T10:17:50+00:00\",\"dateModified\":\"2024-08-12T09:34:03+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.esri.com\/arcgis-blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Using ArcGIS Maps SDK for .NET in an MVVM pattern\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#website\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/\",\"name\":\"ArcGIS Blog\",\"description\":\"Get insider info from Esri product teams\",\"publisher\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.esri.com\/arcgis-blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#organization\",\"name\":\"Esri\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/04\/Esri.png\",\"contentUrl\":\"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/04\/Esri.png\",\"width\":400,\"height\":400,\"caption\":\"Esri\"},\"image\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/esrigis\/\",\"https:\/\/x.com\/ESRI\",\"https:\/\/www.linkedin.com\/company\/5311\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/776665929a66fa840b76139acd87f394\",\"name\":\"Hamish Duff\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/99325835-213x200.jpeg\",\"contentUrl\":\"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/99325835-213x200.jpeg\",\"caption\":\"Hamish Duff\"},\"description\":\"I'm a product engineer on the ArcGIS Maps SDKs for Native Apps team.\",\"url\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Using ArcGIS Maps SDK for .NET in an MVVM pattern","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern","og_locale":"en_US","og_type":"article","og_title":"Using ArcGIS Maps SDK for .NET in an MVVM pattern","og_url":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern","og_site_name":"ArcGIS Blog","article_publisher":"https:\/\/www.facebook.com\/esrigis\/","article_modified_time":"2024-08-12T09:34:03+00:00","twitter_card":"summary_large_image","twitter_site":"@ESRI","twitter_misc":{"Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":["Article","BlogPosting"],"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern#article","isPartOf":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern"},"author":{"name":"Hamish Duff","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/776665929a66fa840b76139acd87f394"},"headline":"Using ArcGIS Maps SDK for .NET in an MVVM pattern","datePublished":"2024-07-26T10:17:50+00:00","dateModified":"2024-08-12T09:34:03+00:00","mainEntityOfPage":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern"},"wordCount":10,"publisher":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#organization"},"keywords":["ArcGIS Developers","ArcGIS Maps SDKs for Native Apps","MVVM","native development"],"articleSection":["Developers"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern","url":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern","name":"Using ArcGIS Maps SDK for .NET in an MVVM pattern","isPartOf":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#website"},"datePublished":"2024-07-26T10:17:50+00:00","dateModified":"2024-08-12T09:34:03+00:00","breadcrumb":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/sdk-net\/developers\/using-arcgis-maps-sdk-for-net-in-an-mvvm-pattern#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.esri.com\/arcgis-blog\/"},{"@type":"ListItem","position":2,"name":"Using ArcGIS Maps SDK for .NET in an MVVM pattern"}]},{"@type":"WebSite","@id":"https:\/\/www.esri.com\/arcgis-blog\/#website","url":"https:\/\/www.esri.com\/arcgis-blog\/","name":"ArcGIS Blog","description":"Get insider info from Esri product teams","publisher":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.esri.com\/arcgis-blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.esri.com\/arcgis-blog\/#organization","name":"Esri","url":"https:\/\/www.esri.com\/arcgis-blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/04\/Esri.png","contentUrl":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/04\/Esri.png","width":400,"height":400,"caption":"Esri"},"image":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/esrigis\/","https:\/\/x.com\/ESRI","https:\/\/www.linkedin.com\/company\/5311\/"]},{"@type":"Person","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/776665929a66fa840b76139acd87f394","name":"Hamish Duff","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/image\/","url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/99325835-213x200.jpeg","contentUrl":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/99325835-213x200.jpeg","caption":"Hamish Duff"},"description":"I'm a product engineer on the ArcGIS Maps SDKs for Native Apps team.","url":""}]}},"text_date":"July 26, 2024","author_name":"Hamish Duff","author_page":false,"custom_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2024\/07\/mvvm_maui_banner.png","primary_product":"ArcGIS Maps SDK for .NET","tag_data":[{"term_id":772172,"name":"ArcGIS Developers","slug":"arcgis-developers","term_group":0,"term_taxonomy_id":772172,"taxonomy":"post_tag","description":"","parent":0,"count":12,"filter":"raw"},{"term_id":768202,"name":"ArcGIS Maps SDKs for Native Apps","slug":"arcgis-maps-sdks-for-native-apps","term_group":0,"term_taxonomy_id":768202,"taxonomy":"post_tag","description":"","parent":0,"count":30,"filter":"raw"},{"term_id":775992,"name":"MVVM","slug":"mvvm","term_group":0,"term_taxonomy_id":775992,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":773362,"name":"native development","slug":"native-development","term_group":0,"term_taxonomy_id":773362,"taxonomy":"post_tag","description":"","parent":0,"count":8,"filter":"raw"}],"category_data":[{"term_id":738191,"name":"Developers","slug":"developers","term_group":0,"term_taxonomy_id":738191,"taxonomy":"category","description":"","parent":0,"count":420,"filter":"raw"}],"product_data":[{"term_id":761642,"name":"ArcGIS Location Platform","slug":"platform","term_group":0,"term_taxonomy_id":761642,"taxonomy":"product","description":"","parent":36601,"count":213,"filter":"raw"},{"term_id":769142,"name":"ArcGIS Maps SDK for .NET","slug":"sdk-net","term_group":0,"term_taxonomy_id":769142,"taxonomy":"product","description":"","parent":36601,"count":39,"filter":"raw"},{"term_id":36601,"name":"Developers","slug":"developers","term_group":0,"term_taxonomy_id":36601,"taxonomy":"product","description":"","parent":0,"count":761,"filter":"raw"}],"primary_product_link":"https:\/\/www.esri.com\/arcgis-blog\/?s=#&products=sdk-net","_links":{"self":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/blog\/2446352","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/blog"}],"about":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/types\/blog"}],"author":[{"embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/users\/359632"}],"replies":[{"embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/comments?post=2446352"}],"version-history":[{"count":0,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/blog\/2446352\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/media?parent=2446352"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/categories?post=2446352"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/tags?post=2446352"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/industry?post=2446352"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/product?post=2446352"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}