Adding a Behavior to a MapView in Xamarin.Forms issue

2198
1
Jump to solution
03-01-2017 09:40 PM
marceloctorres
Esri Contributor

I'm developing a Xamarin.Forms app with ArcGIS Runtime SDK for .NET. I don't want write code at the Code-Behind of my MapPage.xaml Form, then I wrote a Behavior to capture de ViewpointChanged event and then store the value to a property in the ViewModel class.

using Esri.ArcGISRuntime.Xamarin.Forms;
using System;
using System.Windows.Input;
using Xamarin.Forms;

namespace TrabajarMapasPortal.Behaviors
{
  public class MapViewViewpointChangedBehavior : BehaviorBase<MapView>
  {
    /// <summary>
    /// 
    /// </summary>
    public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command",
      typeof(ICommand),
      typeof(MapViewViewpointChangedBehavior));

    /// <summary>
    /// 
    /// </summary>
    public ICommand Command
    {
      get { return (ICommand)GetValue(CommandProperty); }
      set
      {
        SetValue(CommandProperty, value);
      }
    }

    /// <summary>
    /// 
    /// </summary>
    protected override void OnAttachedTo(MapView bindable)
    {
      base.OnAttachedTo(bindable);
      bindable.ViewpointChanged += this.MapViewViewpointChanged;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="bindable"></param>
    protected override void OnDetachingFrom(MapView bindable)
    {
      base.OnDetachingFrom(bindable);
      bindable.ViewpointChanged -= this.MapViewViewpointChanged;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void MapViewViewpointChanged(object sender, EventArgs e)
    {
      if (this.Command != null)
      {
        MapView mapView = (MapView)sender;
        if(mapView != null)
        {
          var viewpoint= mapView.GetCurrentViewpoint(Esri.ArcGISRuntime.Mapping.ViewpointType.BoundingGeometry);
          if (this.Command.CanExecute(viewpoint))
          {
            this.Command.Execute(viewpoint);
          }
        }
      }
    }
  }
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Then I try to attach the behavior to the MapView in the xaml Form:

<?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:esriUI="clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;assembly=Esri.ArcGISRuntime.Xamarin.Forms"
             xmlns:local="clr-namespace:TrabajarMapasPortal.Shared;assembly=TrabajarMapasPortal"
             xmlns:vm="clr-namespace:TrabajarMapasPortal.ViewModels;assembly=TrabajarMapasPortal"
             xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             xmlns:bh="clr-namespace:TrabajarMapasPortal.Behaviors;assembly=TrabajarMapasPortal"
             x:Class="TrabajarMapasPortal.Views.MapPage">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition Height="70"/>
    </Grid.RowDefinitions>
    <esriUI:MapView x:Name="MapView"
                    Map="{Binding Path=Map}">
      <esriUI:Mapview.Behaviors>
        <bh:MapViewViewporChangedBehavior 
          Command="{Binding Path=UpdateViewpointCommand}" />
      </esriUI:Mapview.Behaviors>
    </esriUI:MapView>
    <ListView x:Name="BasemapListBox"
            ItemsSource="{Binding Path=BasemapChoices}"
            Grid.Row="0"
            IsVisible="{Binding Path=IsBasemapListVisible}">
      <ListView.Behaviors>
        <bh:ListViewItemTappedBehavior 
          Command="{Binding Path=ChangeBasemapCommand}"/>
      </ListView.Behaviors>
      <ListView.BackgroundColor>
        <OnPlatform x:TypeArguments="Color"
                    Android="Black"
                    WinPhone="#6FFFFFFF" />
      </ListView.BackgroundColor>
      <ListView.Margin>
        <OnPlatform x:TypeArguments="Thickness" 
                    WinPhone="25" />
      </ListView.Margin>
    </ListView>
    <Grid Grid.Row="1"
        Margin="5,2">
      <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
      </Grid.ColumnDefinitions>
      <Button x:Name="BasemapsButton"
            Grid.Column="0"
            Text="Mapa Base"
            Command="{Binding Path=BaseMapsCommand}" />
      <Button x:Name="NewMapButton"
            Grid.Column="1"
            Text="Nuevo"/>
      <Button x:Name="SaveMapButton"
            Grid.Column="2"
            Text="Guardar ..."
            Command="{Binding Path=ShowSaveMapPageCommand}"/>
    </Grid>
  </Grid>
 </ContentPage>‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

But I get the next error message at compile time:

TrabajarMapasPortal.Views.MapPage.xaml(19,10): error : Position 19:10. 
Type Mapview not found in xmlns clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;
assembly=Esri.ArcGISRuntime.Xamarin.Forms

‍‍‍‍

This is the ViewModel file:

using System;
using System.Windows.Input;
using System.Threading.Tasks;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Portal;
using Esri.ArcGISRuntime.Security;
using Prism.Navigation;
using Prism.Events;
using Prism.Commands;


/// <summary>
/// 
/// </summary>
namespace TrabajarMapasPortal.ViewModels
{
  using Events;
  using Prism.Services;

  /// <summary>
  /// 
  /// </summary>
  public class MapPageViewModel : ViewModelBase
  {
    private Map _map;
    private bool _isBasemapListVisible;

    private const string ArcGISOnlineUrl = "https://www.arcgis.com/sharing/rest";
    public const string AppClientId = "fqCCjw5dkUpIuZba";
    private const string OAuthRedirectUrl = "https://developers.arcgis.com";

    /// <summary>
    /// 
    /// </summary>
    public Map Map
    {
      get { return this._map; }
      set { SetProperty<Map>(ref this._map, value); }
    }

    /// <summary>
    /// 
    /// </summary>
    private Viewpoint CurrentViewpoint { get; set; }

    /// <summary>
    /// 
    /// </summary>
    public bool IsMapSaved
    {
      get { return (this.Map != null && this.Map.Item != null); }
    }

    /// <summary>
    /// 
    /// </summary>
    public string[] BasemapChoices { get; private set; }

    /// <summary>
    /// 
    /// </summary>
    public bool IsBasemapListVisible
    {
      get { return this._isBasemapListVisible; }
      set { SetProperty<bool>(ref this._isBasemapListVisible, value); }
    }

    /// <summary>
    /// 
    /// </summary>
    public ICommand BaseMapsCommand { get; private set; }

    /// <summary>
    /// 
    /// </summary>
    public ICommand ChangeBasemapCommand { get; private set; }

    /// <summary>
    /// 
    /// </summary>
    public ICommand ShowSaveMapPageCommand { get; private set; }

    /// <summary>
    /// 
    /// </summary>
    public ICommand UpdateViewpointCommand { get; private set; }

    /// <summary>
    /// 
    /// </summary>
    public MapPageViewModel(INavigationService navigationService, IEventAggregator eventAgreggator, IPageDialogService dialogService)
    {
      this.DialogService = dialogService;
      this.EventAggregator = eventAgreggator;
      this.NavigationService = navigationService;

      this.EventAggregator
        .GetEvent<SaveMapEvent>()
        .Subscribe(SaveMapAction);

      this.Map = new Map(Basemap.CreateStreetsVector());
      this.BaseMapsCommand = new DelegateCommand(BaseMapsAction);
      this.ChangeBasemapCommand = new DelegateCommand<string>(ChangeBasemapAction);
      this.ShowSaveMapPageCommand = new DelegateCommand(this.ShowSaveMapPageAction);
      this.UpdateViewpointCommand = new DelegateCommand<Viewpoint>(this.UpdateCurrentViewPointAction);
      this.BasemapChoices = new string [] {
            "Topográfico",
            "Topográfico Vector",
            "Calles",
            "Calles Vector",
            "Imágenes",
            "Océanos"};
      this.UpdateAuthenticationManager();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="vp"></param>
    private void UpdateCurrentViewPointAction(Viewpoint vp)
    {
      this.CurrentViewpoint = vp;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="saveMapInfo"></param>
    private async void SaveMapAction(SaveMapInfo saveMapInfo)
    {
      try
      {
        var currentViewpoint = this.Map.InitialViewpoint;
        if (!this.IsMapSaved)
        {
          await SaveNewMapAsync(currentViewpoint, saveMapInfo.Title, saveMapInfo.Description, saveMapInfo.Tags);
          await this.DialogService.DisplayAlertAsync("Mapa Guardado", "Guardado", string.Format("Se guardó {0} en AGOL", saveMapInfo.Title), "OK");
        }
        else
        {
          // TODO: update existing portal item
        }
      }
      catch (Exception ex)
      {
        await this.DialogService.DisplayAlertAsync("No se pudo guardar el mapa", ex.Message, "OK");
      }
    }

    /// <summary>
    /// 
    /// </summary>
    private void BaseMapsAction()
    {
      this.IsBasemapListVisible = true;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="basemap"></param>
    private void ChangeBasemapAction(string basemap)
    {
      switch (basemap)
      {
        case "Topográfico":
          this.Map.Basemap = Basemap.CreateTopographic();
          break;
        case "Topográfico Vector":
          this.Map.Basemap = Basemap.CreateTopographicVector();
          break;
        case "Calles":
          this.Map.Basemap = Basemap.CreateStreets();
          break;
        case "Calles Vector":
          this.Map.Basemap = Basemap.CreateStreetsVector();
          break;
        case "Imágenes":
          this.Map.Basemap = Basemap.CreateImagery();
          break;
        case "Océanos":
          this.Map.Basemap = Basemap.CreateOceans();
          break;
      }
      this.IsBasemapListVisible = false;
    }

    private async void ShowSaveMapPageAction()
    {
      await this.NavigationService.NavigateAsync("SaveMapPage");
      var info = new CredentialRequestInfo
      {
        AuthenticationType = AuthenticationType.Token,
        ServiceUri = new Uri(ArcGISOnlineUrl)
      };
      Credential cred = await AuthenticationManager.Current.GetCredentialAsync(info, false);
      AuthenticationManager.Current.AddCredential(cred);
    }

    public override void OnNavigatedFrom(NavigationParameters parameters)
    {
      base.OnNavigatedFrom(parameters);

    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="initialViewpoint"></param>
    /// <param name="title"></param>
    /// <param name="description"></param>
    /// <param name="tags"></param>
    /// <returns></returns>
    public async Task SaveNewMapAsync(Viewpoint initialViewpoint, string title, string description, string[] tags)
    {
      ArcGISPortal agsOnline = await ArcGISPortal.CreateAsync(new Uri("https://www.arcgis.com/sharing/rest"));
      this.Map.InitialViewpoint = initialViewpoint;
      await this.Map.SaveAsAsync(agsOnline, null, title, description, tags, null);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="info"></param>
    /// <returns></returns>
    public async Task<Credential> CreateCredentialAsync(CredentialRequestInfo info)
    {
      Credential credential = null;

      try
      {
        credential = await AuthenticationManager.Current.GenerateCredentialAsync(info.ServiceUri);
      }
      catch (Exception ex)
      {
        throw (ex);
      }
      return credential;
    }

    /// <summary>
    /// 
    /// </summary>
    private void UpdateAuthenticationManager()
    {
      ServerInfo portalServerInfo = new ServerInfo();
      portalServerInfo.ServerUri = new Uri(ArcGISOnlineUrl);
      portalServerInfo.TokenAuthenticationType = TokenAuthenticationType.OAuthImplicit;

      OAuthClientInfo oAuthInfo = new OAuthClientInfo
      {
        ClientId = AppClientId,
        RedirectUri = new Uri(OAuthRedirectUrl)
      };
      portalServerInfo.OAuthClientInfo = oAuthInfo;
      AuthenticationManager thisAuthenticationManager = AuthenticationManager.Current;
      thisAuthenticationManager.RegisterServer(portalServerInfo);
      thisAuthenticationManager.ChallengeHandler = new ChallengeHandler(CreateCredentialAsync);
    }
  }
}

Appreciate any help!!

Marcelo.

Marcelo César Torres
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
marceloctorres
Esri Contributor

I find the answer after drink a hot cup of coffe!

The <esriUi:Mapview.Behavior> tag was bad written the right way is like that:

<esriUI:MapView.Behaviors>
</esriUI:MapView.Behaviors>‍‍
Marcelo César Torres

View solution in original post

1 Reply
marceloctorres
Esri Contributor

I find the answer after drink a hot cup of coffe!

The <esriUi:Mapview.Behavior> tag was bad written the right way is like that:

<esriUI:MapView.Behaviors>
</esriUI:MapView.Behaviors>‍‍
Marcelo César Torres