Dear All,
i am using mvvm technique.my model class is given below.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" RowSpacing="0" ColumnSpacing="0" BackgroundColor="Transparent">
<esriUI:MapView x:Name="MyMapView" Map="{Binding myMap}" BackgroundColor="GreenYellow" />
</Grid>
<Grid Grid.Row="1" RowSpacing="0" ColumnSpacing="0" BackgroundColor="Transparent">
<Button Text="Direction" BackgroundColor="Black" TextColor="White" Command="{Binding Hello}" />
</Grid>
</Grid>
On my button click i want to ACCESS MyMapView in my model in modelview class.I cant under stand how i get the reference of this MyMapView on event in modelview.because on click event i want to add some graphics on my map.
please help.Thanks in advance.
please help.
Thanks in advance
Solved! Go to Solution.
I'm back. Here I'll explain the solution that I made to WPF now to Xamarin.Forms. First, no implement MVVM pattern easly I used nuget package Prism.Forms from Brian Lagunas. I made the ViewModel class in C# and the View code using XAML in a similar way that in the WPF Project:
This is the ViewModel code:
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.UI;
using Prism.Commands;
using Prism.Mvvm;
using System.Windows.Input;
#if WINDOWS_UWP
using Colors = Windows.UI.Colors;
#else
using Colors = System.Drawing.Color;
#endif
namespace GeonetPost.Xamarin.ViewModels
{
public class MapPageViewModel : BindableBase
{
private Map _myMap;
/// <summary>
///
/// </summary>
public Map MyMap
{
get { return _myMap; }
set { SetProperty(ref _myMap, value); }
}
private GraphicsOverlayCollection _grapchicsOverlays;
/// <summary>
///
/// </summary>
public GraphicsOverlayCollection GraphicsOverlays
{
get { return _grapchicsOverlays; }
set { SetProperty(ref _grapchicsOverlays, value); }
}
private Viewpoint _viewpoint;
/// <summary>
///
/// </summary>
public Viewpoint Viewpoint
{
get { return _viewpoint; }
set { SetProperty(ref _viewpoint, value); }
}
private Viewpoint _updatedViewpoint;
/// <summary>
///
/// </summary>
public Viewpoint UpdatedViewpoint
{
get { return _updatedViewpoint; }
set { SetProperty(ref _updatedViewpoint, value); }
}
/// <summary>
///
/// </summary>
public ICommand ButtonClickCommand { get; private set; }
/// <summary>
///
/// </summary>
public ICommand ZoomCommand { get; private set; }
/// <summary>
///
/// </summary>
public ICommand UpdateViewpointCommand { get; private set; }
/// <summary>
///
/// </summary>
public MapPageViewModel()
{
MyMap = new Map(Basemap.CreateStreets());
GraphicsOverlays = new GraphicsOverlayCollection();
ButtonClickCommand = new DelegateCommand(ButtonClickAction);
ZoomCommand = new DelegateCommand(ZoomAction);
UpdateViewpointCommand = new DelegateCommand<Viewpoint>(UpdateViewpointAction);
GraphicsOverlay go = new GraphicsOverlay()
{
Id = "MyGraphicOverlay"
};
GraphicsOverlays.Add(go);
}
/// <summary>
///
/// </summary>
private void ButtonClickAction()
{
var g = new Graphic()
{
Geometry = new MapPoint(-74, 4, SpatialReferences.Wgs84),
Symbol = new SimpleMarkerSymbol() { Color = Colors.Green, Style = SimpleMarkerSymbolStyle.Circle, Size = 10 }
};
GraphicsOverlays[0].Graphics.Clear();
GraphicsOverlays[0].Graphics.Add(g);
}
/// <summary>
///
/// </summary>
private void ZoomAction()
{
Viewpoint = new Viewpoint(4, -74, 5000000);
}
/// <summary>
///
/// </summary>
/// <param name="vp"></param>
private void UpdateViewpointAction(Viewpoint vp)
{
var projectedVp = new Viewpoint(GeometryEngine.Project(vp.TargetGeometry, SpatialReferences.Wgs84), vp.Camera);
if (UpdatedViewpoint == null || projectedVp.ToJson() != UpdatedViewpoint.ToJson())
{
UpdatedViewpoint = projectedVp;
}
}
}
}
This is the View code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:bh="clr-namespace:GeonetPost.Xamarin.Behaviors"
xmlns:cv="clr-namespace:GeonetPost.Xamarin.Converters"
xmlns:esri="clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;assembly=Esri.ArcGISRuntime.Xamarin.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="GeonetPost.Xamarin.Views.MapPage">
<ContentPage.Resources>
<ResourceDictionary>
<cv:GeographicCoordinateConverter x:Key="GeoCoorConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<esri:MapView Map="{Binding MyMap}"
GraphicsOverlays="{Binding GraphicsOverlays}">
<esri:MapView.Behaviors>
<bh:SetMapViewViewportBehavior Viewpoint="{Binding Viewpoint}" />
<bh:MapViewViewpointChangedBehavior Command="{Binding UpdateViewpointCommand}" />
</esri:MapView.Behaviors>
</esri:MapView>
<Grid Margin="10"
HorizontalOptions="Start"
VerticalOptions="Start"
BackgroundColor="Beige">
<StackLayout Orientation="Vertical"
Margin="10"
HeightRequest="100">
<Label Text="{Binding UpdatedViewpoint.TargetGeometry.XMin, Converter={StaticResource GeoCoorConverter}, ConverterParameter='X', StringFormat='XMin = {0}'}" />
<Label Text="{Binding UpdatedViewpoint.TargetGeometry.YMin, Converter={StaticResource GeoCoorConverter}, ConverterParameter='Y', StringFormat='YMin = {0}'}" />
<Label Text="{Binding UpdatedViewpoint.TargetGeometry.XMax, Converter={StaticResource GeoCoorConverter}, ConverterParameter='X', StringFormat='XMax = {0}'}" />
<Label Text="{Binding UpdatedViewpoint.TargetGeometry.YMax, Converter={StaticResource GeoCoorConverter}, ConverterParameter='Y', StringFormat='YMax = {0}'}" />
</StackLayout>
</Grid>
<StackLayout Grid.Row="1"
Orientation="Horizontal">
<Button Margin="5"
Text="Click me!"
WidthRequest="150"
BackgroundColor="Black"
TextColor="White"
Command="{Binding ButtonClickCommand}" />
<Button Margin="5"
Text="Zoom to Point"
WidthRequest="150"
BackgroundColor="Black"
TextColor="White"
Command="{Binding ZoomCommand}" />
</StackLayout>
</Grid>
</ContentPage>
And this is the code of the SetMapViewViewportBehavior:
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Xamarin.Forms;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace GeonetPost.Xamarin.Behaviors
{
public class SetMapViewViewportBehavior : BehaviorBase<MapView>
{
/// <summary>
///
/// </summary>
public static readonly BindableProperty ViewpointProperty =
BindableProperty.Create(nameof(Viewpoint), typeof(Viewpoint), typeof(SetMapViewViewportBehavior));
/// <summary>
///
/// </summary>
public Viewpoint Viewpoint
{
get { return (Viewpoint)GetValue(ViewpointProperty); }
set { SetValue(ViewpointProperty, value); }
}
/// <summary>
///
/// </summary>
public SetMapViewViewportBehavior()
{
}
/// <summary>
///
/// </summary>
/// <param name="propertyName"></param>
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == nameof(this.Viewpoint))
{
SetViewpoint(this.Viewpoint);
}
}
/// <summary>
///
/// </summary>
/// <param name="vp"></param>
private async void SetViewpoint(Viewpoint vp)
{
if (vp != null)
{
var actualVp = this.AssociatedObject.GetCurrentViewpoint(ViewpointType.BoundingGeometry);
if (actualVp == null || (actualVp != null && !actualVp.Equals(vp)))
{
Debug.WriteLine("SetViewpoint");
await this.AssociatedObject.SetViewpointAsync(vp);
}
}
}
}
}
Note that I had to wrote a BehaviorBase class to implement a Behavior code that in WPF already exits. Now this that the Xamarin.Forms looks like in Windows and Android:
To see the complete solution go to this github repo https://github.com/marceloctorres/GeonetPost.Xamarin
Finally, no make a zoom to your trace route, only set the Viewport property in the MapPageViewModel class with a new Viewport using your polyline extent geometry. For example:
ViewPoint = new Viewpoint(route.Extent);
And the SetMapViewViewportBehavior does the rest.
Marcelo
> On my button click i want to ACCESSÂ MyMapView in my model in modelview class.I
You should never access views in your models and view models. That's breaking with the MVVM pattern.
Your view model should instead expose a Map and a set of GraphicsOverlays, that you then bind to your MapView.
You can then add layers to your map and graphics to your graphics overlays, and the mapview will automatically get them.
<esri:MapView Map="{Binding Map}" GraphicsOverlays="{Binding GraphicsOverlays}" />
Dear Neilsen,
i understand this property GraphicsOverlays.But if i have to call this method .SetViewpointGeometryAsync of mapview object.how i call this in my VeiwModel.
Please help.Thanks in advance.
To implement the MVVM pattern correctly you must consider using a series of objects and interfaces so that the View (XAML code) and the class that acts as ViewModel can communicate effectively. My recommendation is to use a framework that facilitates the writing of the code. You can use Prism.Wpf from Brian Lagunas which is added to the project as a Nuget package.
Here is the code for Vista:
<Window x:Class="GeonetPost.WPF.Views.MapWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<esri:MapView Map="{Binding MyMap}"
GraphicsOverlays="{Binding GraphicsOverlays}">
</esri:MapView>
<Button Grid.Row="1"
Margin="5"
Width="100"
Content="Click Me!"
Background="Black"
Foreground="White"
Command="{Binding ButtonClickCommand}" />
</Grid>
</Window>
You have to bind GraphicsOverlays property of the MapView to a property on your ViewModel Class. Here is the ViewModel Class;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.UI;
using Prism.Commands;
using Prism.Mvvm;
using System.Windows.Input;
using System.Windows.Media;
namespace GeonetPost.WPF.ViewModels
{
public class MapWindowViewModel : BindableBase
{
private Map _myMap;
private GraphicsOverlayCollection _grapchicsOverlays;
/// <summary>
///
/// </summary>
public Map MyMap
{
get { return _myMap; }
set { SetProperty(ref _myMap, value); }
}
/// <summary>
///
/// </summary>
public GraphicsOverlayCollection GraphicsOverlays
{
get { return _grapchicsOverlays; }
set { SetProperty(ref _grapchicsOverlays, value); }
}
/// <summary>
///
/// </summary>
public ICommand ButtonClickCommand { get; private set; }
public MapWindowViewModel()
{
MyMap = new Map(Basemap.CreateStreets());
GraphicsOverlays = new GraphicsOverlayCollection();
ButtonClickCommand = new DelegateCommand(ButtonClickAction);
GraphicsOverlay go = new GraphicsOverlay()
{
Id = "MyGraphicOverlay"
};
GraphicsOverlays.Add(go);
}
/// <summary>
///
/// </summary>
private void ButtonClickAction()
{
var g = new Graphic()
{
Geometry = new MapPoint(-74, 4, SpatialReferences.Wgs84),
Symbol = new SimpleMarkerSymbol() { Color=Colors.Green, Style= SimpleMarkerSymbolStyle.Circle, Size=10 }
};
GraphicsOverlays[0].Graphics.Add(g);
}
}
}
Another thing you have to keep in mind is the handling of events generated by the MapView such as GeoViewTapped or DrawStatusChanged that must be handled with EventTriggers or Behaviors to comply with the MVVM pattern.
Marcelo.
Dear Marcelo,
i understand this property GraphicsOverlays.But if i have to call this method .SetViewpointGeometryAsync of mapview object.how i call this in my VeiwModel.
Please help.Thanks in advance.
Typically your ViewModel will raise an event. For instance add
public event EventHandler<Viewpoint> RequestViewpoint;
You then just raise the event. If a listener is active, it'll act appropriately on it. For instance in your code-behind you can write view-specific code like:
myViewModel.RequestViewpoint += (sender,viewpoint) => mapView.SetViewpointAsync(viewpoint);
(just make sure you unhook from that event when navigating away from the page, or there's' a good chance you'll leak a lot of memory each time you leave the map page).
If you absolutely want no code-behind (although most people agree view-specific code like this is ok in MVVM), you can use attached properties to accomplish the same thing. This is a little more complicated subject if you're new to XAML, but it's used here: https://github.com/Esri/arcgis-runtime-demos-dotnet/blob/master/src/TurnByTurn/RoutingSample.Univers...
by is using this common class:
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.UI.Controls;
using System.Diagnostics;
using System.Windows;
using System.Windows.Interactivity;
namespace GeonetPost.WPF.Behaviors
{
public class SetMapViewViewportBehavior : Behavior<MapView>
{
/// <summary>
///
/// </summary>
public static readonly DependencyProperty ViewpointProperty =
DependencyProperty.Register(nameof(Viewpoint), typeof(Viewpoint), typeof(SetMapViewViewportBehavior));
/// <summary>
///
/// </summary>
public Viewpoint Viewpoint
{
get { return (Viewpoint)GetValue(ViewpointProperty); }
set { SetValue(ViewpointProperty, value); }
}
public SetMapViewViewportBehavior()
{
Debug.WriteLine("Hola");
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if(e.Property.Name == nameof(Viewpoint))
{
SetViewpoint(this.Viewpoint);
}
}
/// <summary>
///
/// </summary>
/// <param name="vp"></param>
private async void SetViewpoint(Viewpoint vp)
{
if (vp != null)
{
var actualVp = this.AssociatedObject.GetCurrentViewpoint(ViewpointType.BoundingGeometry);
if (actualVp == null || (actualVp != null && !actualVp.Equals(vp)))
{
Debug.WriteLine("SetViewpoint");
await this.AssociatedObject.SetViewpointAsync(vp);
}
}
}
}
}
private Viewpoint _viewpoint;
/// <summary>
///
/// </summary>
public Viewpoint Viewpoint
{
get { return _viewpoint; }
set { SetProperty(ref _viewpoint, value); }
}
2) A command:
/// <summary>
///
/// </summary>
public ICommand ButtonClickCommand { get; private set; }
/// <summary>
///
/// </summary>
public ICommand ZoomCommand { get; private set; }
public MapWindowViewModel()
{
MyMap = new Map(Basemap.CreateStreets());
GraphicsOverlays = new GraphicsOverlayCollection();
ButtonClickCommand = new DelegateCommand(ButtonClickAction);
ZoomCommand = new DelegateCommand(ZoomAction);
GraphicsOverlay go = new GraphicsOverlay()
{
Id = "MyGraphicOverlay"
};
GraphicsOverlays.Add(go);
}
3) And an action:
/// <summary>
///
/// </summary>
private void ZoomAction()
{
Viewpoint = new Viewpoint(4, -74, 5000000);
}
Finally, we modify the View to looks like this:
<Window x:Class="GeonetPost.WPF.Views.MapWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:bh="clr-namespace:GeonetPost.WPF.Behaviors"
xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<esri:MapView Map="{Binding MyMap}"
GraphicsOverlays="{Binding GraphicsOverlays}">
<ei:Interaction.Behaviors>
<bh:SetMapViewViewportBehavior Viewpoint="{Binding Viewpoint}" />
</ei:Interaction.Behaviors>
</esri:MapView>
<StackPanel Grid.Row="1"
Orientation="Horizontal">
<Button Margin="5"
Width="100"
Content="Click Me!"
Background="Black"
Foreground="White"
Command="{Binding ButtonClickCommand}" />
<Button Margin="5"
Width="100"
Content="Zoom to point!"
Background="Black"
Foreground="White"
Command="{Binding ZoomCommand}" />
</StackPanel>
</Grid>
</Window>
You can find the complete code in this github site: https://github.com/marceloctorres/GeonetPost.WPF.
I just add a MapViewViewpointChangedBehavior to handle ViewpointChanged event of the MapView and a Converter to show Envelope coordinates of the Viewpoint in DMS format.... please check here https://github.com/marceloctorres/GeonetPost.WPF
Dear Marcelo Cesar Torres,
Your code is in WPF.I ma using Xamarin forms.i am facing problem converting form WPF to xamarin form.
Can you help.
Thanks in advance.
I'm working on translate the sample to Xamarin.Forms...