How to manage(hide) Back Button and Master Page in navigation while Deeplinking? - xamarin.forms

On HomePage of Button Update Profile, it redirects and working fine. But when I try to go to Update Profile page from any other place like DeepLink, it shows Back Button with a word Master Page. Can anybody please suggest me what I am missing here?
HomePage(Master)
<?xml version="1.0" encoding="UTF-8"?>
<local:MasterDetailPageWithLifecycle xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyProject;assembly=MyProject"
x:Class="MyProject.HomePage"
OnAppearingCommand="{Binding OnAppearingCommand}"
Title="Master Page">
<MasterDetailPage.Master>
<ContentPage Title="Home page" Icon="hamburger.png">
<ContentPage.Resources>
<ResourceDictionary>
<local:MenuItemDataTemplateSelector x:Key="menuItemDataTemplateSelector" HighlitedTemplate="{StaticResource highlitedTemplate}"
NormalTemplate="{StaticResource normalTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout BackgroundColor="{DynamicResource d8Purple}" VerticalOptions="FillAndExpand" Padding="0, 48, 0, 0">
<StackLayout Padding="0, 40, 0, 0" Spacing="0">
<ListView x:Name="listView" Margin="0,9,0,0" VerticalOptions="FillAndExpand" SeparatorVisibility="None"
ItemSelected="OnItemSelected" ItemTemplate="{StaticResource menuItemDataTemplateSelector}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</local:MasterDetailPageWithLifecycle>
HomePage.cs
public HomePage()
{
InitializeComponent();
BindingContext =_vm = App.Locator.Home;
NavigationPage.SetHasNavigationBar(this, false);
_masterPageItems = new List<MasterPageItem>();
_masterPageItems.Add(new MasterPageItem
{
Title = "Update Profile",
TargetType = nameof(EditProfilePage)
});
listView.ItemsSource = _masterPageItems;
}
public void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
var name = item.TargetType;
if (name == "EditProfilePage")
{
Detail = new MyProjectNavigationPage(new EditProfilePage());
listView.SelectedItem = null;
IsPresented = false;
}
}
}
public class MyProjectNavigationPage : NavigationPage
{
public MyProjectNavigationPage(Page root) : base(root)
{
if (Device.OS == TargetPlatform.iOS)
{
BarTextColor = Colors.d8Grey;
BarBackgroundColor = Color.White;
Title = root.Title;
}
}
}
EditProfile XAML
<?xml version="1.0" encoding="UTF-8"?>
<local:ContentPageWithCustomBackButton
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyProject;assembly=MyProject"
x:Class="MyProject.EditProfilePage"
OnAppearingCommand="{Binding OnAppearingCommand}"
Title="Update Profile">
<ContentPage.Content>
<Grid RowSpacing="0">
//Design content
</Grid>
</ContentPage.Content>
</local:ContentPageWithCustomBackButton>
EditProfile CS
public EditProfilePage()
{
InitializeComponent();
BindingContext=_editProfileViewModel = App.Locator.EditProfile;
_editProfileViewModel.PropertyChanged += ViewModel_PropertyChanged;
}
EditProfileDeeplink
public override void Navigate(string uri)
{
_navigationService.NavigateTo(nameof(EditProfilePage));
}

It's just because of when your trying to Navigate from HomePage(Masterpage) to EditProfile Page you set EditProfile Page as Master Detaill Page Like,
if (name == "EditProfilePage")
{
Detail = new MyProjectNavigationPage(new EditProfilePage());
listView.SelectedItem = null;
IsPresented = false;
}
but when you Come from other page you Only Navigate to that Page Like,
_navigationService.NavigateTo(nameof(EditProfilePage));
So you have to handle this Navigation by set page as MasterDetail(DetailPage) Like,
App.Current.MainPage = new MenuMaster {Detail = new NavigationPage(new EditProfile())};

With presenters :
Create class for your presenter
public class IosPagePresenter : MvxFormsIosViewPresenter
{
public override void Show(MvxViewModelRequest request)
{
if (request.PresentationValues?["NavigationCommand"] == "StackClear")
FormsApplication.MainPage = new ContentPage();
base.Show(request);
}
public IosPagePresenter(IUIApplicationDelegate applicationDelegate, UIWindow window, MvxFormsApplication formsApplication) : base(applicationDelegate, window, formsApplication)
{
}
}
Register this presenter in the setup.IOS
protected override IMvxIosViewPresenter CreatePresenter()
{
var presenter = new IosPagePresenter(ApplicationDelegate, Window, FormsApplication);
Mvx.RegisterSingleton<IMvxFormsViewPresenter>(presenter);
return presenter;
}
And call from ViewModel
var bundle = new MvxBundle(new Dictionary<string, string> { { "NavigationCommand", "StackClear" } });
await _navigationService.Navigate<SavedTankViewModel>(bundle);

Related

Set Placeholder as Title on Picker Item Selection in Xamarin Forms

Need a custom picker in which the Title reduces in size once an Item is selected from the Picker as in image. Same happens for a MaterialisedEntry but need the same for Picker Control
Xamarin.Forms does not allow you to inject a custom view (like a ContentView with a stacklayout) into the Picker control). What you could do is to use a Grid where you have a stacklayout overlaid on a Picker. Based on the SelectedItem property of the picker you would need to update the Text of the State Label.
<Grid HorizontalOptions="Center" VerticalOptions="Center">
<Picker x:Name="picker"
Title="select a state">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Colorado</x:String>
<x:String>California</x:String>
<x:String>Ohio</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
<StackLayout WidthRequest="300" InputTransparent="True" BackgroundColor="White">
<Label Text="State" FontSize="10"/>
<!--Here you would need to bind the Label Text to a property
that changes according to the SelectedItem of the picker-->
<Label Text="Colorado" FontSize="14"/>
</StackLayout>
</Grid>
According to your description, I suggest you can use custom render to add arrow for Picker control, like this:
Create class name CustomPicker in PLC.
public class CustomPicker : Picker
{
public static readonly BindableProperty ImageProperty =
BindableProperty.Create(nameof(Image), typeof(string), typeof(CustomPicker), string.Empty);
public string Image
{
get { return (string)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
}
Create class name CustomPickerRenderer in Android or ios.
[assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))]
namespace demo3.Droid
{
public class CustomPickerRenderer : PickerRenderer
{
CustomPicker element;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
element = (CustomPicker)this.Element;
if (Control != null && this.Element != null && !string.IsNullOrEmpty(element.Image))
{
Control.Background = AddPickerStyles(element.Image);
}
}
public LayerDrawable AddPickerStyles(string imagePath)
{
//ShapeDrawable border = new ShapeDrawable();
// border.Paint.Color = Android.Graphics.Color.Gray;
// border.SetPadding(10,10,10,10);
// border.Paint.SetStyle(Paint.Style.Stroke);
//Drawable[] layers = { border , GetDrawable(imagePath) };
Drawable[] layers = { GetDrawable(imagePath) };
LayerDrawable layerDrawable = new LayerDrawable(layers);
layerDrawable.SetLayerInset(0, 0, 0, 0, 0);
return layerDrawable;
}
private BitmapDrawable GetDrawable(string imagePath)
{
int resID = Resources.GetIdentifier(imagePath, "drawable", this.Context.PackageName);
var drawable = ContextCompat.GetDrawable(this.Context, resID);
var bitmap = ((BitmapDrawable)drawable).Bitmap;
var result = new BitmapDrawable(Resources, Bitmap.CreateScaledBitmap(bitmap, 70, 70, true));
result.Gravity = Android.Views.GravityFlags.Right;
return result;
}
}
}
3.Reference this customPicker in PLC.
<Frame
Padding="8"
BorderColor="Gray"
CornerRadius="20"
HasShadow="True"
IsClippedToBounds="True">
<StackLayout>
<Label
x:Name="label"
FontSize="20"
Text="state" />
<picker:CustomPicker
x:Name="picker1"
Title="select one item"
Image="ic_arrow_drop_down"
SelectedIndexChanged="Picker1_SelectedIndexChanged">
<picker:CustomPicker.Items>
<x:String>1</x:String>
<x:String>2</x:String>
</picker:CustomPicker.Items>
</picker:CustomPicker>
</StackLayout>
</Frame>
private void Picker1_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = sender as CustomPicker;
if(picker.SelectedIndex>-1)
{
label.FontSize = 10;
}
}
This is the screenshot:

How can I create a TabbedPage from data brought from a web service without blocking the user interface?

I am trying to create a TabbedPage within a MasterDetailPage from data brought from a web service.
The issue with this is that I am blocking the user interface because I have to wait for the task that JSON brings to then iterate it and create the corresponding ViewModels for each ContentPage of the TabbedPage.
My code is something like:
Mi TabbePage:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyProject.Views.MyTabbedPage"
ItemSource={Binding Tabs}>
<TabbedPage.ItemTemplate>
<DataTemplate>
<ContentPage Title={Binding Description}>
...
</ContentPage>
</DataTemplate>
</TabbedPage.ItemTemplate>
</TabbedPage>
My TabbedPageViewModel:
public TabbedPageViewModel()
{
var task = Task.Run(async () => { await LoadThings(); });
Task.WaitAll(task);
Tabs = new ObservableCollection<TabbedPageDetailViewModel>();
foreach (var t in Things)
{
Tabs.Add(new TabbedPageDetailViewModel(t.IdCode, t.Description));
}
}
private async Task LoadThings()
{
Things = new List<Thing>(await App.WebApiManager.GetCustomerThingsAsync(App.User.IdCustomer));
}
My TabbedPageDetailViewModel:
public TabbedPageDetailViewModel(string idCode, string description)
{
IdCode = idCode;
Description = description;
Task task1 = Task.Run(async () => await LoadTask1(idCode));
var task2 = Task.Run(async () => { await LoadTask2(idCode); });
Task.WaitAll(task1, task2);
}
private async Task LoadTask1(string idCode)
{
//await code that brings API data to load controls
}
private async Task LoadTask(string idCode)
{
// await code that brings API data to load controls
}
All this code blocks the user interface while the data is consumed from the API.
How can I implement this in a correct synchronous way which allows my interface to remain reactive?
PS: I can download my collection of Things in the login process of my app but I still get some delay when TabbedPageDetailViewModel is being instantiated.
How about add the activityindicator in the contentPage? For example:
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="App526.TabbedPage1">
<!--Pages can be added as references or inline-->
<ContentPage Title="{Binding des}" >
<ActivityIndicator IsRunning="true" x:Name="page1ActIndictor" />
</ContentPage>
<ContentPage Title="{Binding name}" >
<ActivityIndicator IsRunning="true" x:Name="page2ActIndictor" />
</ContentPage>
<ContentPage Title="{Binding age}" >
<ActivityIndicator IsRunning="true" x:Name="page3ActIndictor" />
</ContentPage>
</TabbedPage>
And in your code behind, use messagingCenter to stop the activityindicator :
public TabbedPage1()
{
InitializeComponent();
MessagingCenter.Subscribe<Object>(this, "dataLoadFinish", (sender) =>
{
page1ActIndictor.IsRunning = false;
page2ActIndictor.IsRunning = false;
page3ActIndictor.IsRunning = false;
});
}
Send the message when loading finished:
public class TabbedPageDetailViewModel
{
//...
public void loadingTask() {
//After finish loading data
MessagingCenter.Send<Object>(this, "dataLoadFinish");
}
//...
}

Loading FFImageLoading SVG into ImageButton Source from code?

This post shows how to make SvgCachedImage act like a button. However, how to load SvgCachedImage into XamarinForm's ImageButton Source ?
My non-working 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:local="clr-namespace:SharedSvgSample"
x:Class="SharedSvgSample.MainPage"
xmlns:ffimageloadingsvg="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms">
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<ImageButton
x:Name="myButton"
HeightRequest="200"
Clicked="myButton_Clicked"
WidthRequest="200" />
</StackLayout>
</ContentPage>
Code-behind:
using FFImageLoading.Svg.Forms;
using System;
using Xamarin.Forms;
namespace SharedSvgSample
{
public partial class MainPage : ContentPage
{
private bool _myButtonValue;
private SvgImageSource _visibilityOn = null;
private SvgImageSource _visibilityOff = null;
public MainPage()
{
InitializeComponent();
_visibilityOn = SvgImageSource.FromResource("SharedSvgSample.Resources.visibility_on.svg");
_visibilityOn.VectorHeight = 100;
_visibilityOn.VectorWidth = 100;
_visibilityOff = SvgImageSource.FromResource("SharedSvgSample.Resources.visibility_off.svg");
_visibilityOff.VectorHeight = 100;
_visibilityOff.VectorWidth = 100;
myButton.Source = _visibilityOff;
}
private void myButton_Clicked(object sender, EventArgs e)
{
_myButtonValue = !_myButtonValue;
myButton.Source = _myButtonValue ? _visibilityOn.ImageSource : _visibilityOff.ImageSource;
}
}
}
Unluckily, Xamarin.Forms.Button only supports FileImageSource, so at the moment you can't just load an SVG into the Button Image.
However, you can just load the SVG image, and add the TapGestureRecognizer to simulate a Button.

Xamarin Listview don't show the observable Collection

I'm using Xamarin.Forms MVVM to develop my app, and don't found what I'm doing wrong, I have an ObservableCollection with the values from web API, and when I set a break point all the values are good even in the view when I see the values of the binding source everything have the value, but the values are not showing up in my ListView.
Here is the ViewModel
class DatosMedicosViewModel : BaseViewModel
{
private ApiService apiService;
private ObservableCollection<Land> land;
private bool isRefreshing;
public ObservableCollection<Land> Lands
{
get { return this.land; }
set { SetValue(ref this.land, value); }
}
public bool IsRefreshing
{
get { return this.isRefreshing; }
set { SetValue(ref this.isRefreshing, value); }
}
public DatosMedicosViewModel()
{
this.apiService = new ApiService();
this.LoadLand();
}
private async void LoadLand()
{
this.IsRefreshing = true;
var connection = await this.apiService.CheckConnection();
if (!connection.IsSuccess)
{
this.IsRefreshing = false;
await Application.Current.MainPage.DisplayAlert(
"Error",
connection.Message,
"Accept");
await Application.Current.MainPage.Navigation.PopAsync();
return;
}
var response = await this.apiService.GetList<Land>(
"url Base",
"prefix",
"Controller");
if (!response.IsSuccess)
{
this.IsRefreshing = false;
await Application.Current.MainPage.DisplayAlert(
"Error",
response.Message,
"Accept"
);
return;
}
var list = (List<Land>)response.Result;
this.Lands = new ObservableCollection<Land>(list);
this.IsRefreshing = false;
}
public ICommand RefreshCommand
{
get
{
return new RelayCommand(LoadLand);
}
}
}
Here is the View
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ARLAPP.Views.ConsultaPage"
BackgroundColor="White"
BindingContext="{Binding Main, Source={StaticResource Locator}}"
Title="Lands">
<ContentPage.Content>
<StackLayout
BindingContext="{Binding Lands}"
Padding="5">
<StackLayout>
<Image
VerticalOptions="Center"
WidthRequest="300"
Source="UserIcon"
BackgroundColor="Transparent"/>
<Label Text="Mark"
VerticalOptions="Center"
HorizontalOptions="CenterAndExpand"
FontAttributes="Bold"
FontSize="Medium"/>
</StackLayout>
<StackLayout>
<ListView
SeparatorVisibility="Default"
FlowDirection="LeftToRight"
BackgroundColor="White"
ItemsSource="{Binding Lands}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label
Grid.Column="2"
VerticalOptions="Center"
TextColor="Black"
Text="{Binding Currency}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Here how I call the view
if (this.PageName == "Lands")
{
MainViewModel.GetInstance().Lands= new LandViewModel();
Application.Current.MainPage = new LandMasterPage();
}
Check your BindingContext. I think you are setting it wrong in your view.
In your top-level StackLayout you set the the BindingContext to your property: BindingContext="{Binding Lands}". And in your ListView you set the ItemsSource also to this property: ItemsSource="{Binding Lands}". That won't work because the ListView is trying to bind to a property Lands inside your BindingContext, which is also set to Lands.
Remove the BindingContext from your top-level StackLayout, because you don't need it.
Ensure the BindingContext of your page ConsultaPage is set to your view-model DatosMedicosViewModel.
Sample of setting the bindingcontext (abstract code):
var mypage = new ConsultaPage();
mypage.BindingContext = new DatosMedicosViewModel();
await Navigation.PushAsync(mypage);
// Load your data in OnAppearing() of the page-event
This should solve your binding-problem.
Side-Note: As Abdul Gani said in the comments: Ensure you implement the INotifyPropertyChanged interface, but I assume you do this already in your BaseViewModel and call the NotifyChanged-Event in your SetValue-Method.

ArcGis Map does not disapear in navigation Xamarin.Forms

Hello guys :D I am having a problem with the android part of the Xamarin.Forms
When I Navigate from a AbsoluteLayout with a Map and TabbleView (enter image description here) to a Grid with only a Map, the Map from the previous page stays static on top of the second one(enter image description here). This problem does not manifest in iOS, only in Android. If any of you guys know the problem please tell me so I can quickly fix :D
Page with First Map
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SGPI.Intervencao.CriarIntervencao"
xmlns:local="clr-namespace:SGPI.Shared"
xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;assembly=Esri.ArcGISRuntime.Xamarin.Forms"
Padding="5,5">
<ContentPage.Resources>
<ResourceDictionary>
<local:MapViewModel x:Key="MapViewModel" />
</ResourceDictionary>
</ContentPage.Resources>
<AbsoluteLayout>
<esriUI:MapView Map="{Binding Map, Source={StaticResource MapViewModel}}" x:Name="map" AbsoluteLayout.LayoutBounds="0,0, 1, 0.6" AbsoluteLayout.LayoutFlags="All"/>
<TableView Intent="Form" HasUnevenRows="True" AbsoluteLayout.LayoutBounds="0,1, 1, 0.4" AbsoluteLayout.LayoutFlags="All">
<TableRoot>
<TableSection Title="Information">
<EntryCell Label="Nome" Text="{Binding Name}" Placeholder="Nome"/>
<EntryCell Label="Codigo" Text="{Binding Code}" Placeholder="Codigo"/>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="Status" HorizontalOptions="Start" VerticalOptions="Center"/>
<Picker x:Name="pick" SelectedIndex="{Binding Index}" SelectedItem="{Binding Status}" Title="Status" HorizontalOptions="FillAndExpand">
<Picker.Items>
<x:String>Em Construção</x:String>
<x:String>Construido</x:String>
</Picker.Items>
</Picker>
</StackLayout>
</ViewCell>
<ViewCell>
<Button Image="editMap.png" Clicked="Button_Clicked" />
</ViewCell>
</TableSection>
</TableRoot>
</TableView>
</AbsoluteLayout>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="dan.png" Order="Primary" x:Name="done" Clicked="Done"/>
</ContentPage.ToolbarItems>
Code Behind
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace SGPI.Intervencao
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CriarIntervencao : ContentPage
{
public event EventHandler<IntervencaoClass> IntervencaoAdded;
public event EventHandler<IntervencaoClass> IntervencaoUpdated;
public CriarIntervencao(IntervencaoClass intervencao)
{
if(intervencao == null)
throw new ArgumentNullException(nameof(intervencao));
InitializeComponent();
BindingContext = new IntervencaoClass
{
Code = intervencao.Code,
Name = intervencao.Name,
Status = intervencao.Status,
Index = intervencao.Index,
Id = intervencao.Id,
Polygons = intervencao.Polygons
};
if(intervencao.Polygons != null)
map.GraphicsOverlays.Add(intervencao.Polygons);
}
private async void Done(object sender, EventArgs e)
{
var intervencao = BindingContext as IntervencaoClass;
if (filled())
{
await DisplayAlert("Erro", "Preenche tudo", "OK");
return;
}
map.GraphicsOverlays.Clear();
if (!intervencao.Id.HasValue)
{
intervencao.Id = 1;
IntervencaoAdded?.Invoke(this, intervencao);
}
else
{
IntervencaoUpdated?.Invoke(this, intervencao);
}
await Navigation.PopAsync();
}
public bool filled()
{
var intervencao = BindingContext as IntervencaoClass;
return String.IsNullOrEmpty(intervencao.Name) || String.IsNullOrEmpty(intervencao.Code) || pick.SelectedIndex == -1;
}
private async void Button_Clicked(object sender, EventArgs e)
{
map.GraphicsOverlays.Clear();
var page = new MapPages.MapPage((BindingContext as IntervencaoClass).Polygons);
page.AcceptedMap += (send, graphics) => {
var intervencao = BindingContext as IntervencaoClass;
intervencao.Polygons = graphics;
map.GraphicsOverlays.Add(graphics);
Navigation.PopAsync();
};
await Navigation.PushAsync(page);
}
}
}
Second Page
<?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:local="clr-namespace:SGPI.Shared"
xmlns:esriUI="clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;assembly=Esri.ArcGISRuntime.Xamarin.Forms"
x:Class="SGPI.MapPages.MapPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MapViewModel x:Key="MapViewModel" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<esriUI:MapView Map="{Binding Map, Source={StaticResource MapViewModel}}" x:Name="map" GeoViewTapped="Map_GeoViewTapped"/>
</Grid>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="dan.png" Clicked="Done"/>
</ContentPage.ToolbarItems>
</ContentPage>
Second Page Code Behind
using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Symbology;
using System.Collections.Generic;
using Xamarin.Forms;
using Esri.ArcGISRuntime.UI;
using System;
using System.Threading;
namespace SGPI.MapPages
{
public partial class MapPage : ContentPage
{
public event EventHandler<GraphicsOverlay> AcceptedMap;
List<MapPoint> points;
static SimpleLineSymbol symbol = new SimpleLineSymbol()
{
Style = SimpleLineSymbolStyle.Dash,
Color = System.Drawing.Color.Black,
Width = 1
};
static SimpleMarkerSymbol marker = new SimpleMarkerSymbol()
{
Color = System.Drawing.Color.Pink,
Outline = symbol,
Style = SimpleMarkerSymbolStyle.Diamond,
Size = 10
};
static SimpleLineSymbol line = new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.CadetBlue, 1);
static SimpleFillSymbol fill = new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.Color.Black, line);
public MapPage(GraphicsOverlay graphic)
{
InitializeComponent();
map.Map = new SGPI.Shared.MapViewModel().Map;
if(graphic == null)
graphic = new GraphicsOverlay();
map.GraphicsOverlays.Add(graphic);
points = new List<MapPoint>();
}
private Graphic AddPolygonInMap(MapPoint[] points)
{
var pointCollection = new PointCollection(points[0].SpatialReference);
foreach (MapPoint p in points)
pointCollection.Add(p);
var sPolygon = new Polygon(pointCollection);
return new Graphic(sPolygon, fill);
}
private void AddPointinMap(MapPoint point)
{
Graphic graphic = new Graphic(point, marker);
map.GraphicsOverlays[0].Graphics.Add(graphic);
}
private async void Map_GeoViewTapped(object sender, Esri.ArcGISRuntime.Xamarin.Forms.GeoViewInputEventArgs e)
{
var tolerance = 10d; // Use larger tolerance for touch
var maximumResults = 1; // Only return one graphic
var onlyReturnPopups = false; // Don't return only popups
// Use the following method to identify graphics in a specific graphics overlay
IdentifyGraphicsOverlayResult identifyResults = await map.IdentifyGraphicsOverlayAsync(
map.GraphicsOverlays[0],
e.Position,
tolerance,
onlyReturnPopups,
maximumResults);
// Check if we got results
if (identifyResults.Graphics.Count > 0)
{
// Make sure that the UI changes are done in the UI thread
Device.BeginInvokeOnMainThread(async () => {
await DisplayAlert("", "Tapped on graphic", "OK");
});
} else
{
points.Add(e.Location);
AddPointinMap(e.Location);
}
}
private async void Done(object sender, EventArgs e)
{
if(points.Count > 2) {
GraphicsOverlay graphics = new GraphicsOverlay();
graphics.Graphics.Add(AddPolygonInMap(points.ToArray()));
map.GraphicsOverlays.Add(graphics);
Thread.Sleep(500);
var accepted = await DisplayAlert("Aviso", "Este é o polígono certo?", "Sim", "Não");
if (accepted)
{
GraphicsOverlay graph = graphics;
map.GraphicsOverlays.Clear();
AcceptedMap?.Invoke(this, graph);
}
else
{
for(int i = 1; i < map.GraphicsOverlays.Count; i++)
map.GraphicsOverlays[i] = new GraphicsOverlay();
}
}
}
}
}

Resources