In a Xamarin.Forms app, I have a button on a Page (Page_1) that sends me to another Page (Page_2) with the statement
await Navigation.PushAsync (new Page_2 ());
On this Page, I have several ImageButtons that all return me to Page_1 when clicked with the statement
await Navigation.PopAsync ();
This works the first time around, but the second time I press the button on the Page_1, I go to the Page_2, then when I click on an ImageButton the application quits with the error message:
System.ArgumentNullException
Message = Buffer cannot be null.
Parameter name: buffer
How can I make the Navigation.NavigationStack still contain the Page_1?
Thanks a lot for your help.
Add for Deczaloth : this is the partial code of the first page :
private async void btnModifImage_Clicked(object sender, EventArgs e)
{
string imgLocation;
// affichage et choix : gallerie ou internet
imgLocation = await DisplayActionSheet("Emplacement", "Annuler", null, imageLocation);
if (imgLocation == "Gallerie")
{
// chargement image depuis gallerie
imageChanged = true;
article_nomcat = pickerCat.Title;
article_unit = pickerUnit.Title;
try
{
FileData fileData = await CrossFilePicker.Current.PickFile();
//
var filepath = fileData.FilePath;
//
if (fileData == null)
return; // user canceled file picking
newimageByte = fileData.DataArray;
resizedImageByte = await ImageResizer.ResizeImage(newimageByte, 100, 100);
if (resizedImageByte != null)
{
imageChanged = true;
var stream1 = new MemoryStream(resizedImageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
}
catch (Exception ex)
{
imageChanged = false;
}
}
else
{
App.ImageName = name.Text;
await Navigation.PushAsync(new ImageBingPage());
}
}
and the partial code of the second page ImageBingPage :
private async void img1_Clicked(object sender, EventArgs e)
{
byte[] img1Byte = await DownloadImageAsync(img1Path);
App.imgByte = img3Byte;
await Navigation.PopAsync();
}
And the biginning of the second page :
protected override void OnAppearing()
{
base.OnAppearing();
SearchImage();
base.OnDisappearing();
}
public void SearchImage()
{
List<Images> imageList = new List<Images>();
SearchResult bingResult = BingImageSearch(App.ImageName);
var res = JsonConvert.DeserializeObject<ResponseModel>(bingResult.jsonResult);
for (int i = 0; i < res.value.Count; i++)
{
imageList.Add(new Images { ImagePath = res.value[i].contentUrl });
}
img1.Source = ImageSource.FromUri(new Uri(imageList[0].ImagePath));
img1Path = imageList[0].ImagePath;
img2.Source = ImageSource.FromUri(new Uri(imageList[1].ImagePath));
img2Path = imageList[1].ImagePath;
img3.Source = ImageSource.FromUri(new Uri(imageList[2].ImagePath));
img3Path = imageList[2].ImagePath;
}
static SearchResult BingImageSearch(string searchQuery)
{
var uriQuery = baseURl + "?q=" + Uri.EscapeDataString(searchQuery);
WebRequest request = HttpWebRequest.Create(uriQuery);
request.Headers["Ocp-Apim-Subscription-Key"] = APIKey;
HttpWebResponse response = (HttpWebResponse)request.GetResponseAsync().Result;
string json = new StreamReader(response.GetResponseStream()).ReadToEnd();
var searchResult = new SearchResult();
searchResult.jsonResult = json;
searchResult.relevantHeaders = new Dictionary<String, String>();
foreach (String header in response.Headers)
{
if (header.StartsWith("BingAPIs-") || header.StartsWith("X-MSEdge-"))
searchResult.relevantHeaders[header] = response.Headers[header];
}
return searchResult;
}
async Task<byte[]> DownloadImageAsync(string imageUrl)
{
try
{
using (var httpResponse = await _httpClient.GetAsync(imageUrl))
{
if (httpResponse.StatusCode == HttpStatusCode.OK)
{
return await httpResponse.Content.ReadAsByteArrayAsync();
}
else
{
//Url is Invalid
return null;
}
}
}
catch (Exception e)
{
//Handle Exception
return null;
}
}
Add 2 for Deczaloth : You're right: the stack is not empty (see the two attached screenshots). So where does the problem come from?
Edit for Jack : I have modified my code to simplify the problem:
private async void img1_Clicked(object sender, EventArgs e)
{
//byte[] img1Byte = await DownloadImageAsync(imgPath[0]);
//App.imgByte = img1Byte;
await Navigation.PopAsync();
}
private async void img2_Clicked(object sender, EventArgs e)
{
//byte[] img2Byte = await DownloadImageAsync(imgPath[1]);
//App.imgByte = img2Byte;
await Navigation.PopAsync();
}
And now I can do 3 round trips between the ArticleEntry page and the ImageBingPage page, but after, the app crashes anyway.
Edit for Jack : there is the code on the page ArticleEntry :
protected override async void OnAppearing()
{
base.OnAppearing();
if (App.AddArticle == true)
{
ToolbarItems.Remove(btnDelete);
}
//
_article = (Article)BindingContext;
old_article_ID = _article.ID;
old_article_name = _article.Name;
article_nomcat = _article.NomCat;
article_unit = _article.UniteMesure;
//
if (_article.ID == 0)
{
name.Focus();
}
//
if (pickerCat.Title != null & pickerCat.Title != "choisissez une catégorie")
{
_article.NomCat = pickerCat.Title;
pickerCat.TitleColor = Color.Black;
}
else
{
if (_article.NomCat != null)
{
pickerCat.Title = _article.NomCat;
pickerCat.SelectedItem = _article.NomCat;
pickerCat.TitleColor = Color.Black;
}
else
{
pickerCat.Title = "choisissez une catégorie";
pickerCat.TitleColor = Color.Gray;
}
}
//
if (pickerUnit.Title != null & pickerUnit.Title != "choisissez une unité de mesure")
{
_article.UniteMesure = pickerUnit.Title;
pickerUnit.TitleColor = Color.Black;
}
else
{
if (_article.UniteMesure != null)
{
pickerUnit.Title = _article.UniteMesure;
pickerUnit.SelectedItem = _article.UniteMesure;
pickerUnit.TitleColor = Color.Black;
}
else
{
pickerUnit.Title = "choisissez une unité de mesure";
pickerUnit.TitleColor = Color.Gray;
}
}
//
if (pickerListeAchats.Title != null & pickerListeAchats.Title != "choisissez une liste d'achats")
{
//_article.UniteMesure = pickerUnit.Title;
pickerListeAchats.TitleColor = Color.Black;
}
//
ReadAllCategorieList dbcategorie = new ReadAllCategorieList();
DB_CategorieList = dbcategorie.GetAllCategorie(); //Get all DB Categorie
// trier
DB_CategorieList_Sorted = DB_CategorieList.OrderBy(i => i.NomCat).ToList();
//
// rempliir la liste du picker Cat
string[] pickerListCat;
List<string> pkListCat = new List<string>();
foreach (var item in DB_CategorieList_Sorted)
{
string element = item.NomCat;
//pickerCat.Items.Add(element);
pkListCat.Add(element);
}
pickerListCat = pkListCat.ToArray();
pickerCat.ItemsSource = pickerListCat;
//
// rempliir la liste du picker Unit
ReadAllUnitList dbunit = new ReadAllUnitList();
DB_UnitList = dbunit.GetAllUnit();
// trier
DB_UnitList_Sorted = DB_UnitList.OrderBy(i => i.UniteMesure).ToList();
//
List<string> pickerListUnit = new List<string>();
foreach (var item in DB_UnitList_Sorted)
{
string element = item.UniteMesure;
pickerListUnit.Add(element);
}
pickerUnit.ItemsSource = pickerListUnit;
//
// remplir la liste du picker ListeAchats
ReadAllListeAchatsList dblisteachats = new ReadAllListeAchatsList();
DB_ListeAchatsList = dblisteachats.GetAllListeAchats();
// trier
DB_ListeAchatsList_Sorted = DB_ListeAchatsList.OrderBy(i => i.NomListe).ToList();
//
List<string> pickerListAchat = new List<string>();
foreach (var item in DB_ListeAchatsList_Sorted)
{
string element = item.NomListe;
pickerListAchat.Add(element);
}
pickerListeAchats.ItemsSource = pickerListAchat;
//
//
imageByte = _article.ImageArt;
//
// convertir le byte[] en bitmapimage (si != 0)
if (_article.ImageArt != null)
{
if (imageChanged)
{
var stream1 = new MemoryStream(newimageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
else
{
var stream1 = new MemoryStream(imageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
}
else
{
if (imageChanged == false)
{
imageByte = ImageDataFromResource("MemoCourses.Assets.Images.appareil_photo.png");
var stream1 = new MemoryStream(imageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
}
//
if (App.imgByte != null)
{
resizedImageByte = await ImageResizer.ResizeImage(App.imgByte, 100, 100);
if (resizedImageByte != null)
{
imageChanged = true;
var stream1 = new MemoryStream(resizedImageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
}
//
base.OnDisappearing();
App.AddArticle = false;
}
Code changed based on Jack's comments
protected override async void OnAppearing()
{
base.OnAppearing();
if (App.AddArticle == true)
{
ToolbarItems.Remove(btnDelete);
}
//
_article = (Article)BindingContext;
old_article_ID = _article.ID;
old_article_name = _article.Name;
article_nomcat = _article.NomCat;
article_unit = _article.UniteMesure;
//
if (_article.ID == 0)
{
name.Focus();
}
//
if (pickerCat.Title != null & pickerCat.Title != "choisissez une catégorie")
{
_article.NomCat = pickerCat.Title;
pickerCat.TitleColor = Color.Black;
}
else
{
if (_article.NomCat != null)
{
pickerCat.Title = _article.NomCat;
pickerCat.SelectedItem = _article.NomCat;
pickerCat.TitleColor = Color.Black;
}
else
{
pickerCat.Title = "choisissez une catégorie";
pickerCat.TitleColor = Color.Gray;
}
}
//
if (pickerUnit.Title != null & pickerUnit.Title != "choisissez une unité de mesure")
{
_article.UniteMesure = pickerUnit.Title;
pickerUnit.TitleColor = Color.Black;
}
else
{
if (_article.UniteMesure != null)
{
pickerUnit.Title = _article.UniteMesure;
pickerUnit.SelectedItem = _article.UniteMesure;
pickerUnit.TitleColor = Color.Black;
}
else
{
pickerUnit.Title = "choisissez une unité de mesure";
pickerUnit.TitleColor = Color.Gray;
}
}
//
if (pickerListeAchats.Title != null & pickerListeAchats.Title != "choisissez une liste d'achats")
{
pickerListeAchats.TitleColor = Color.Black;
}
//
ReadAllCategorieList dbcategorie = new ReadAllCategorieList();
DB_CategorieList = dbcategorie.GetAllCategorie(); //Get all DB Categorie
// trier
DB_CategorieList_Sorted = DB_CategorieList.OrderBy(i => i.NomCat).ToList();
//
// rempliir la liste du picker Cat
string[] pickerListCat;
List<string> pkListCat = new List<string>();
foreach (var item in DB_CategorieList_Sorted)
{
string element = item.NomCat;
pkListCat.Add(element);
}
pickerListCat = pkListCat.ToArray();
pickerCat.ItemsSource = pickerListCat;
//
// rempliir la liste du picker Unit
ReadAllUnitList dbunit = new ReadAllUnitList();
DB_UnitList = dbunit.GetAllUnit();
// trier
DB_UnitList_Sorted = DB_UnitList.OrderBy(i => i.UniteMesure).ToList();
//
List<string> pickerListUnit = new List<string>();
foreach (var item in DB_UnitList_Sorted)
{
string element = item.UniteMesure;
pickerListUnit.Add(element);
}
pickerUnit.ItemsSource = pickerListUnit;
//
// remplir la liste du picker ListeAchats
ReadAllListeAchatsList dblisteachats = new ReadAllListeAchatsList();
DB_ListeAchatsList = dblisteachats.GetAllListeAchats();
// trier
DB_ListeAchatsList_Sorted = DB_ListeAchatsList.OrderBy(i => i.NomListe).ToList();
//
List<string> pickerListAchat = new List<string>();
foreach (var item in DB_ListeAchatsList_Sorted)
{
string element = item.NomListe;
pickerListAchat.Add(element);
}
pickerListeAchats.ItemsSource = pickerListAchat;
//
//
imageByte = _article.ImageArt;
//
// convertir le byte[] en bitmapimage (si != 0)
if (_article.ImageArt != null)
{
if (imageGallery)
{
var stream1 = new MemoryStream(resizedImageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
else
{
if (App.imgByte == null)
{
// image depuis la base SQLite
var stream1 = new MemoryStream(imageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
else
{
// image depuis internet
var stream1 = new MemoryStream(App.imgByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
}
}
else
{
if (imageChanged == false)
{
// image par défaut
imageByte = ImageDataFromResource("MemoCourses.Assets.Images.appareil_photo.png");
var stream1 = new MemoryStream(imageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
}
//
if (App.imgByte != null & imageInternet == true)
{
resizedImageByte = await ImageResizer.ResizeImage(App.imgByte, 100, 100);
if (resizedImageByte != null)
{
var stream1 = new MemoryStream(resizedImageByte);
ImageArticle.Source = ImageSource.FromStream(() => stream1);
}
}
//
base.OnDisappearing();
App.AddArticle = false;
imageGallery = false;
imageInternet = false;
}
The problem is not caused by the NavigationStack.
1.You should not call base.OnDisappearing(); in OnAppearing:
protected override void OnAppearing()
{
base.OnAppearing();
SearchImage();
//remove this line
//base.OnDisappearing();
}
2.You get a java.io.IOException: Cleartext HTTP traffic to aardbeienzo.com not permitted which you does not have permission to load url start with http:
Go to Android project --> Properties --> Assemblyinfo.cs, add UsesCleartextTraffic = true:
// Add some common permissions, these can be removed if not needed
[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]
[assembly: Application(UsesCleartextTraffic = true)]
Update:
private async void img1_Clicked(object sender, EventArgs e)
{
byte[] img1Byte = await DownloadImageAsync(img1Path);
//App.imgByte = img3Byte;
App.imgByte = img1Byte;
await Navigation.PopAsync();
}
Update 2:
Generally, If you use Navigation push/pop correctly, it will not cause the error. Pay attention to the OnAppearing method of Page and check the code there.
The correct code has been updated in the question by Marcel Delhaye.
Related
I am creating a cross platform school app. I have a page that displays the students in a class and their pictures. I added a functionality that enables the teacher to change the profile of the student or add one if none. the thing is that this functionality works great on android in which they can change or add multiple profiles for different students at once. but on ios, something seems wrong. If they add multiple pics, the pictures won't be uploaded to my database and thus no pics are displayed, but if they add only one pic for a student, it will be uploaded.
Here is my code:
private void generateProfiles(double width, List<studentdetailsformanagment> lstofstudents,List<absenteesIds>attending)
{
studentprofiles.Children.Clear();
dtAttedndance.Clear();
double nbofcolumns = width / 180;
double nbofrows = lstofstudents.Count / nbofcolumns;
for (int i = 0; i < nbofcolumns-1; i++)
{
studentprofiles.ColumnDefinitions.Add(new ColumnDefinition { Width = 170 });
}
for (int j = 0; j < nbofrows + 1; j++)
{
studentprofiles.RowDefinitions.Add(new RowDefinition { Height = 170 });
}
for (int i = 0; i < lstofstudents.Count; i++)
{
var student = lstofstudents[i];
DataRow dtdr = dt.Select("[id] ='" + student.id + "'").FirstOrDefault();
if (dtdr == null)
dt.Rows.Add(Preferences.Get("AcademicYearId", ""),student.id, section.Text, student.profilePic);
SwipeItem delete = new SwipeItem
{
Text = "Remove",
BackgroundColor = Color.Transparent
};
SwipeItems items = new SwipeItems
{
Mode = SwipeMode.Execute
};
items.Add(delete);
List<SwipeItem> swipeItems = new List<SwipeItem>() { delete };
Image img = new Image
{
Source = ImageSource.FromResource("ALNahrainAlphaApp.icons.user_profile.png"),
Aspect = Aspect.AspectFill,
Margin = -20,
HeightRequest = 100,
WidthRequest = 100,
};
if (student.profilePic == null)
img.Source = ImageSource.FromResource("ALNahrainAlphaApp.icons.user_profile.png");
else
{
byte[] bytes = System.Convert.FromBase64String(student.profilePic);
img.Source = ImageSource.FromStream(() => new MemoryStream(bytes));
}
StackLayout stdinfo = new StackLayout { Orientation = StackOrientation.Vertical, ClassId="false" };
if (attending.Count > 0)
{
for (int j = 0; j < attending.Count; j++)
{
if (student.id == attending[j].id)
{
stdinfo.BackgroundColor = Color.FromHex("#0d98ba");
stdinfo.ClassId = "true";
}
}
}
Label stdname = new Label { TextColor = Color.Black, Text = student.name, VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center };
Frame circleImageFrame = new Frame
{
ClassId = student.id,
Margin = 10,
BorderColor = Color.Black,
CornerRadius = 50,
HeightRequest = 60,
WidthRequest = 60,
IsClippedToBounds = true,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Content = img
};
stdinfo.Children.Add(circleImageFrame);
stdinfo.Children.Add(stdname);
SwipeView swipe = new SwipeView
{
Threshold=70,
LeftItems = new SwipeItems(items),
Content = stdinfo,
};
AddTap.Tapped += async (s, evt) =>
{
try
{
await PickAndShow(null, img);
byte[] profilepic = System.IO.File.ReadAllBytes(imgpath);
string imageBase64 = Convert.ToBase64String(profilepic);
try
{
DataRow dr = dt.Select("[id] ='" + circleImageFrame.ClassId + "'").FirstOrDefault();
if (dr != null) dr["profilePic"] = imageBase64;
var obj = stdprofiles.FirstOrDefault(x => x.id == circleImageFrame.ClassId);
if (obj != null) obj.profilePic = imageBase64;
}
catch (Exception exp)
{
await DisplayAlert("Error", exp.Message, "ok");
}
}
catch(Exception exp)
{
}
};
circleImageFrame.GestureRecognizers.Add(AddTap);
this is the function i use to display circular frames with profiles.
async Task<FileResult> PickAndShow(PickOptions options, Image image)
{
imgpath = "";
try
{
var result = await FilePicker.PickAsync(options);
if (result != null)
{
Text = $"File Name: {result.FileName}";
if (result.FileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ||
result.FileName.EndsWith("png", StringComparison.OrdinalIgnoreCase))
{
stream = await result.OpenReadAsync();
image.Source = ImageSource.FromStream(() => stream);
}
}
imgpath = result.FullPath;
return result;
}
catch (Exception ex)
{
}
return null;
}
and this is the function i use to pick an image from the folders in the device
this is the code that runs when i press submit:
async private void submit_Clicked(object sender, EventArgs e)
{
submit.IsEnabled = false;
if (flagEditorAttednd == true)
{
flagEditorAttednd = false;
edit.Source = ImageSource.FromResource("ALNahrainAlphaApp.icons.edit.png");
select.IsEnabled = false;
urlClass urldata = new urlClass();
string uri = urldata.url + "/PostStdDetails";
StringContent content = new StringContent(JsonConvert.SerializeObject(dt), Encoding.UTF8, "application/json");
try
{
HttpResponseMessage responsepost = await client.PostAsync(uri, content);
if (responsepost.IsSuccessStatusCode == true)
{
string outcome = await responsepost.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<string>(outcome);
await DisplayAlert("Students Distribution", "Students in section " + section.Text + " were edited successfully", "OK");
var width = this.Width;
generateProfiles(width, stdprofiles, absentstds);
nbofstds.Text = stdprofiles.Count.ToString() + " Students";
submit.IsEnabled = true;
}
else
{
await DisplayAlert("Operation Failed", "Response Failed!", "Cancel");
submit.IsEnabled = true;
}
}
catch (System.Net.WebException exp)
{
submit.IsEnabled = true;
bool ans = await DisplayAlert("Connection Failed", "Please Check Your Internet Connection!", "Retry", "Cancel");
if (ans == true)
submit_Clicked(sender, e);
}
catch (Exception exp)
{
submit.IsEnabled = true;
bool ans = await DisplayAlert("Connection Failed", exp.Message, "Retry", "Cancel");
if (ans == true)
submit_Clicked(sender, e);
}
}
this is the code at the sql server side:
ALTER PROCEDURE [dbo].[getstddetailsupdate]
#stdDetailsTable as StdDetailsTypeTable readonly
AS
Begin
update [dbo].[students]
set [section]= X.section, [profilePic]=X.profilePic
FROM [dbo].[students],#stdDetailsTable X where [dbo].[students].id=X.id and [dbo].[students].YearId=X.YearId
end
this is how they appear:
when they tap on the circular frame, they can choose a pic and when they click submit, the pics chosen for each student will be uploaded. the problem is that when many pics are chosen, no pictures are uploaded. but if only one is chosen, it will be uploaded normally. why is that? i can't figure out why this is happening. thanks in advance
I'm using Xamarin.Forms.Map and I want to show pins on my map with already expanded window message(without click on them). Something like screenshot. By default window message show only after I clicked on them. How can I do this?
I did a test based on this sample:sample
The thing I do is override the GetViewForAnnotation method.
I add a subview and set it's position based on pin's position.
Here is relative code:
[assembly:ExportRenderer(typeof(CustomMap),typeof(CustomMapRenderer))]
namespace My_Forms_Test3.iOS
{
public class CustomMapRenderer:MapRenderer
{
UIView customPinView;
List<CustomPin> customPins;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
var nativeMap = Control as MKMapView;
nativeMap.GetViewForAnnotation = null;
nativeMap.CalloutAccessoryControlTapped -= OnCallourAccessoryControlTapped;
nativeMap.DidSelectAnnotationView -= OnDidSelect;
nativeMap.DidDeselectAnnotationView -= OnDidDeSelect;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
var nativeMap = Control as MKMapView;
customPins = formsMap.CustomPins;
nativeMap.GetViewForAnnotation = GetViewForAnnotation;
nativeMap.CalloutAccessoryControlTapped += OnCallourAccessoryControlTapped;
nativeMap.DidSelectAnnotationView += OnDidSelect;
nativeMap.DidDeselectAnnotationView += OnDidDeSelect;
}
}
private void OnDidDeSelect(object sender, MKAnnotationViewEventArgs e)
{
if (!e.View.Selected)
{
customPinView.RemoveFromSuperview();
customPinView.Dispose();
customPinView = null;
}
}
private void OnDidSelect(object sender, MKAnnotationViewEventArgs e)
{
throw new NotImplementedException();
}
private void OnCallourAccessoryControlTapped(object sender, MKMapViewAccessoryTappedEventArgs e)
{
throw new NotImplementedException();
}
protected override MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
{
MKAnnotationView annotationView = null;
if (annotation is MKUserLocation)
return null;
var customPin = GetCustomPin(annotation as MKPointAnnotation);
if (customPin == null)
{
throw new Exception("not found");
}
annotationView = mapView.DequeueReusableAnnotation(customPin.Name);
if (annotationView == null)
{
annotationView = new CustomMKAnnotationView(annotation, customPin.Name);
annotationView.Image = UIImage.FromFile("pin.png");
annotationView.CalloutOffset = new CGPoint(0, 0);
annotationView.LeftCalloutAccessoryView = new UIImageView(UIImage.FromFile("monkey.png"));
annotationView.RightCalloutAccessoryView = UIButton.FromType(UIButtonType.DetailDisclosure);
((CustomMKAnnotationView)annotationView).Name = customPin.Name;
customPinView = new UIView();
var Label = new UILabel();
Label.Text = "Samsung";
Label.Frame=new CGRect(annotationView.GetFrame().X+35,annotationView.GetFrame().Y,100,50);
var Label2 = new UILabel();
Label2.Text = "20:20";
Label2.Frame = new CGRect(annotationView.GetFrame().X + 35, annotationView.GetFrame().Y+20, 100, 50);
customPinView.Frame= new CGRect(annotationView.GetFrame().X+40, annotationView.GetFrame().Y-20, 100, 50);
customPinView.AddSubview(Label);
customPinView.AddSubview(Label2);
Label.BaselineAdjustment = UIBaselineAdjustment.AlignBaselines;
customPinView.BackgroundColor = UIColor.White;
customPinView.Layer.CornerRadius = 5;
customPinView.Alpha = (nfloat)0.8;
customPinView.Layer.MasksToBounds = true;
annotationView.AddSubview(customPinView);
}
annotationView.CanShowCallout = true;
return annotationView;
}
CustomPin GetCustomPin(MKPointAnnotation annotation)
{
var position = new Position(annotation.Coordinate.Latitude, annotation.Coordinate.Longitude);
foreach (var pin in customPins)
{
if (pin.Position == position)
{ return pin; }
}
return null;
}}
result:
I have a created a custom Picker with downarrow image at right side using UITextFied in Xamarin ios. When I click the downarrow, the picker is not opening. But when the click centre of the UITextField, the picker is opening. How to open the pickerview when click of downarrow?
[assembly: ExportRenderer(typeof(CustomMonthPicker), typeof(CustomMonthPickerRenderer))]
namespace AMS.iOS.CustomRenderer
{
public class CustomMonthPickerRenderer : ViewRenderer<CustomMonthPicker, UITextField>
{
private DateTime _selectedDate;
private UITextField _dateLabel;
private PickerDateModel _pickerModel;
protected override void OnElementChanged(ElementChangedEventArgs<CustomMonthPicker> e)
{
try
{
base.OnElementChanged(e);
_dateLabel = new UITextField();
var dateToday = Element.Date;
SetupPicker(new DateTime(dateToday.Year, dateToday.Month, 1));
SetNativeControl(_dateLabel);
Control.EditingChanged += ControlOnEditingChanged;
Element.PropertyChanged += Element_PropertyChanged;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private void ControlOnEditingChanged(object sender, EventArgs e)
{
if (Element.Date.ToString().Equals(DateTime.MinValue.ToString()))
{
_dateLabel.Text = "";
}
else
{
var monthName = SetMonthNumberToMonthName(Element.Date.Month);
var currentDate = $"{monthName} | {Element.Date.Year}";
if (_dateLabel.Text != currentDate)
{
_dateLabel.Text = currentDate;
}
}
}
protected override void Dispose(bool disposing)
{
Element.PropertyChanged -= Element_PropertyChanged;
base.Dispose(disposing);
}
private void SetupPicker(DateTime date)
{
var datePicker = new UIPickerView();
_pickerModel = new PickerDateModel(datePicker, date);
datePicker.ShowSelectionIndicator = true;
_selectedDate = date;
_pickerModel.PickerChanged += (sender, e) =>
{
_selectedDate = e;
};
datePicker.Model = _pickerModel;
//_pickerModel.MaxDate = Element.MaxDate ?? DateTime.MaxValue;
//_pickerModel.MinDate = Element.MinDate ?? DateTime.MinValue;
var toolbar = new UIToolbar
{
BarStyle = UIBarStyle.Default,
Translucent = true
};
toolbar.SizeToFit();
var doneButton = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done,
(s, e) =>
{
Element.Date = _selectedDate;
if (_selectedDate == DateTime.MinValue)
{
Element.Date = DateTime.Now;
}
var monthNameText = SetMonthNumberToMonthName(Element.Date.Month);
_dateLabel.Text = $"{monthNameText} | {Element.Date.Year}";
MessagingCenter.Send<App>((App)Xamarin.Forms.Application.Current, "PreferredDateChanged");
_dateLabel.ResignFirstResponder();
});
toolbar.SetItems(new[] { new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace), doneButton }, true);
Element.Date = _selectedDate;
var monthName = SetMonthNumberToMonthName(Element.Date.Month);
//if (Element.Date.Equals(DateTime.MinValue.ToString()))
//{
// _dateLabel.Text = "";
//}
//else
if (Element.Date.Year == 1)
{
_dateLabel.Text = "";
}
else
_dateLabel.Text = $"{monthName} | {Element.Date.Year}";
_dateLabel.InputAccessoryView = toolbar;
_dateLabel.TextColor = Element.TextColor.ToUIColor();
_dateLabel.VerticalAlignment = UIControlContentVerticalAlignment.Fill;
_dateLabel.HorizontalAlignment = UIControlContentHorizontalAlignment.Fill;
_dateLabel.TextAlignment = (UITextAlignment)TextAlignment.Center;
var downarrow = UIImage.FromBundle("brandIcon.png");
CGSize iconSize = downarrow.Size;
if (20 > -1)
iconSize = new CGSize((float)20, (float)20);
UIView paddingView = new UIView(new CGRect(0, 0, iconSize.Width + 8, iconSize.Height + 8));
UIImageView sideView = new UIImageView(new CGRect(0, 4, iconSize.Width, iconSize.Height));
sideView.Image = downarrow;
paddingView.AddSubview(sideView);
paddingView.UserInteractionEnabled = true;
_dateLabel.RightViewMode = UITextFieldViewMode.Always;
_dateLabel.RightView = paddingView;
//var gesture = new UITapGestureRecognizer(()=> {
// if (datePicker != null)
// {
// //datePicker.Hidden = !datePicker.Hidden;
// _dateLabel.InputView.Hidden = !_dateLabel.InputView.Hidden;
// //_dateLabel.AccessibilityRespondsToUserInteraction = true;
// }
//});
//paddingView.AddGestureRecognizer(gesture);
_dateLabel.RightView.UserInteractionEnabled = true;
// _dateLabel.RightView.AddGestureRecognizer(gesture);
_dateLabel.InputView = datePicker;
}
private void Element_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
try
{
if (e.PropertyName == CustomMonthPicker.MaxDateProperty.PropertyName)
{
_pickerModel.MaxDate = Element.MaxDate ?? DateTime.MinValue;
}
else if (e.PropertyName == CustomMonthPicker.MinDateProperty.PropertyName)
{
_pickerModel.MinDate = Element.MinDate ?? DateTime.MaxValue;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
How to open the pickerview when click of downarrow?
As ToolmakerSteve mentioned , we can add a tap gesture on the icon to focus the textfiled and it will open picker view automacially .
Try the following code
UIView paddingView = new UIView(new CGRect(0, 0, iconSize.Width + 8, iconSize.Height + 8));
UIImageView sideView = new UIImageView(new CGRect(0, 4, iconSize.Width, iconSize.Height));
sideView.Image = downarrow;
paddingView.AddSubview(sideView);
paddingView.UserInteractionEnabled = true;
_dateLabel.RightViewMode = UITextFieldViewMode.Always;
_dateLabel.RightView = paddingView;
//add this
sideView.UserInteractionEnabled = true;
UITapGestureRecognizer tap = new UITapGestureRecognizer(()=> {
_dateLabel.BecomeFirstResponder();
});
paddingView.AddGestureRecognizer(tap);
I have followed this link Xamarin.Forms WKWebView to inject Javascript into WebView for iOS. It has worked until Xamarin.Forms 3.3.
In Xamarin.Forms 3.3, the default custom renderer for iOS can be changed from UIWebView to WKWebView. I have followed the changes in AssemblyInfo.cs. Xamarin.Forms 3.3.0. Unfortunately, the changes break the codes.
Below are the changes
//protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
//if (Control == null)
//{
userController = new WKUserContentController();
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
//var config = new WKWebViewConfiguration { UserContentController = userController };
webView = NativeView as WKWebView;
webView.Configuration.UserContentController = userController;
webView.WeakUIDelegate = Self;
view = Element as BibleWebView;
//webView = (WKWebView)Control;
// var cgRect = new CoreGraphics.CGRect(view.X, view.Y, view.WidthRequest, view.HeightRequest);
// webView = new WKWebView(cgRect, config)
// {
// WeakUIDelegate = Self,
// };
// SetNativeControl(webView);
//}
//if (e.OldElement != null)
//{
// userController.RemoveAllUserScripts();
// userController.RemoveScriptMessageHandler("invokeAction");
// var hybridWebView = e.OldElement as BibleWebView;
// hybridWebView.Cleanup();
// e.OldElement.ShowPopup -= OnShowPopup;
//}
//if (Control != null)
//{
// BibleWebView webview = Element as BibleWebView;
HtmlWebViewSource htmlSource = (HtmlWebViewSource)view.Source;
string html = htmlSource.Html;
webView.LoadHtmlString(new NSString(html), NSBundle.MainBundle.ResourceUrl);
view.ShowPopup += OnShowPopup;
//}
}
The old codes are commented and the new codes are uncommented. Any help will be much appreciated.
I'm using a custom renderer with Xamarin.Forms 3.3 using EvaluateJavaScript to load javascript in a loaded page.
HybridWebView is a class in my shared project that inherit from Xamarin.Forms.WebView
First option
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>
{
WKUserContentController userController;
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var formsWebView = sender as WebView;
if (formsWebView != null)
{
userController = new WKUserContentController();
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView(Frame, config);
SetNativeControl(webView);
Control.AllowsBackForwardNavigationGestures = true;
Control.NavigationDelegate = new CustomWebViewClient(Element);
if((formsWebView.Source as UrlWebViewSource) != null)
{
string url = System.Web.HttpUtility.UrlPathEncode((formsWebView.Source as UrlWebViewSource).Url);
Control.LoadRequest(new NSUrlRequest(new NSUrl(url)));
}
else if((formsWebView.Source as HtmlWebViewSource) != null)
{
Control.LoadHtmlString((formsWebView.Source as HtmlWebViewSource).Html, new NSUrl(""));
}
}
}
public class CustomWebViewClient : WKNavigationDelegate, INSUrlConnectionDataDelegate
{
private HybridWebView _webclient;
private WKWebView _webView;
public CustomWebViewClient(HybridWebView webclient)
{
_webclient = webclient;
}
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
string allowZoom = #"javascript:
var div = document.createElement('div');
div.setAttribute('id', 'div1');
div.innerHTML = 'Test';
document.getElementsByClassName('container')[0].appendChild(div);
div.onclick = document.getElementById('div1').onclick = function() { div.innerHTML = 'Change text'; }";
webView.EvaluateJavaScript(allowZoom, null);
_webView = webView;
}
}
}
Second option
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var formsWebView = sender as WebView;
string allowZoom = #"javascript:
var div = document.createElement('div');
div.setAttribute('id', 'div1');
div.innerHTML = 'Test';
document.getElementsByClassName('container')[0].appendChild(div);
div.onclick = document.getElementById('div1').onclick = function() { div.innerHTML = 'Change text'; }";
if (formsWebView != null)
{
userController = new WKUserContentController();
//Using WKUserScript
var script = new WKUserScript(new NSString(allowZoom), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView(Frame, config);
SetNativeControl(webView);
Control.AllowsBackForwardNavigationGestures = true;
Control.NavigationDelegate = new CustomWebViewClient(Element);
if((formsWebView.Source as UrlWebViewSource) != null)
{
string url = System.Web.HttpUtility.UrlPathEncode((formsWebView.Source as UrlWebViewSource).Url);
Control.LoadRequest(new NSUrlRequest(new NSUrl(url)));
}
else if((formsWebView.Source as HtmlWebViewSource) != null)
{
Control.LoadHtmlString((formsWebView.Source as HtmlWebViewSource).Html, new NSUrl(""));
}
}
}
In one of the apps I'm working on I require the use of custom map pins and I've followed the guide on Xamarin https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/map/customized-pin/ as well as borrowed their sample code to try and make my own example.
It works to a degree in such that the info window is actually updated to the custom layout but the map pin never changes.
My CustomMapRenderer:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Android.Content;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Maps.Android;
using WorkingWithMaps.Droid.Renderers;
using WorkingWithMaps;
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace WorkingWithMaps.Droid.Renderers
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter, IOnMapReadyCallback
{
GoogleMap map;
List<CustomPin> customPins;
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
map.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
((MapView)Control).GetMapAsync(this);
}
}
void IOnMapReadyCallback.OnMapReady(GoogleMap googleMap)
{
map = googleMap;
map.SetInfoWindowAdapter(this);
map.InfoWindowClick += OnInfoWindowClick;
this.NativeMap = googleMap;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
map.Clear();
foreach (var pin in customPins)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
map.AddMarker(marker);
}
isDrawn = true;
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
public Android.Views.View GetInfoContents(Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (customPin.Id == "Xamarin")
{
view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
}
else
{
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
}
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
return view;
}
return null;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
}
}
and my map page, also heavily borrowed from Xamarin's working with maps guide
using Plugin.Geolocator;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Xaml;
namespace WorkingWithMaps
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainPage : ContentPage
{
CustomMap map;
Geocoder geoCoder;
String navAdd;
public MainPage()
{
InitializeComponent();
var maplocator = CrossGeolocator.Current;
maplocator.DesiredAccuracy = 1;
geoCoder = new Geocoder();
map = new CustomMap
{
HeightRequest = 100,
WidthRequest = 960,
VerticalOptions = LayoutOptions.FillAndExpand,
IsShowingUser = true
};
map.MapType = MapType.Street;
map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(55.237208, 10.479160), Distance.FromMeters(500)));
map.IsShowingUser = true;
var street = new Button { Text = "Street" };
var hybrid = new Button { Text = "Hybrid" };
var satellite = new Button { Text = "Satellite" };
street.Clicked += HandleClickedAsync;
hybrid.Clicked += HandleClickedAsync;
//satellite.Clicked += OnReverseGeocodeButtonClicked;
var segments = new StackLayout
{
Spacing = 30,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Orientation = StackOrientation.Horizontal,
Children = { street, hybrid, satellite }
};
Content = new StackLayout
{
HorizontalOptions = LayoutOptions.Center,
Children = { map, segments }
};
Device.BeginInvokeOnMainThread(async () =>
{
try
{
//var currentpos = await maplocator.GetPositionAsync(1000);
//map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(currentpos.Latitude, currentpos.Longitude), Distance.FromMeters(500)));
if (!maplocator.IsListening)
{
await maplocator.StartListeningAsync(1000, 50, true);
}
}
catch (Exception ex)
{
Debug.WriteLine("Fail" + ex);
}
});
var pin = new CustomPin
{
Pin = new Pin
{
Type = PinType.Place,
Position = new Position(55.240121, 10.469895),
Label = "Testing Pins"
}
};
map.CustomPins = new List<CustomPin> { pin };
map.Pins.Add(pin.Pin);
map.PropertyChanged += (sender, e) =>
{
Debug.WriteLine(e.PropertyName + " just changed!");
if (e.PropertyName == "VisibleRegion" && map.VisibleRegion != null)
CalculateBoundingCoordinates(map.VisibleRegion);
};
maplocator.PositionChanged += (sender, e) =>
{
var position = e.Position;
map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(position.Latitude, position.Longitude), Distance.FromKilometers(2)));
};
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
//async void OnReverseGeocodeButtonClicked(object sender, EventArgs e)
//{
// var possibleAddresses = await geoCoder.GetAddressesForPositionAsync(pin.Position);
// navAdd += possibleAddresses.ElementAt(0) + "\n";
// switch (Device.OS)
// {
// case TargetPlatform.iOS:
// Device.OpenUri(new Uri(string.Format("http://maps.apple.com/?q={0}", WebUtility.UrlEncode(navAdd))));
// break;
// case TargetPlatform.Android:
// Device.OpenUri(new Uri(string.Format("geo:0,0?q={0}", WebUtility.UrlEncode(navAdd))));
// break;
// case TargetPlatform.Windows:
// case TargetPlatform.WinPhone:
// Device.OpenUri(new Uri(string.Format("bingmaps:?where={0}", Uri.EscapeDataString(navAdd))));
// break;
// }
//}
void HandleClickedAsync(object sender, EventArgs e)
{
var b = sender as Button;
switch (b.Text)
{
case "Street":
map.MapType = MapType.Street;
break;
case "Hybrid":
map.MapType = MapType.Hybrid;
break;
case "Satellite":
map.MapType = MapType.Satellite;
break;
}
}
static void CalculateBoundingCoordinates(MapSpan region)
{
var center = region.Center;
var halfheightDegrees = region.LatitudeDegrees / 2;
var halfwidthDegrees = region.LongitudeDegrees / 2;
var left = center.Longitude - halfwidthDegrees;
var right = center.Longitude + halfwidthDegrees;
var top = center.Latitude + halfheightDegrees;
var bottom = center.Latitude - halfheightDegrees;
if (left < -180) left = 180 + (180 + left);
if (right > 180) right = (right - 180) - 180;
Debug.WriteLine("Bounding box:");
Debug.WriteLine(" " + top);
Debug.WriteLine(" " + left + " " + right);
Debug.WriteLine(" " + bottom);
}
}
}
On top of the mentioned issue the implementation has also caused IsShowingUser = True to no longer function as well as
var currentpos = await maplocator.GetPositionAsync(1000);
to throw an exception.
Github repository: https://github.com/Mortp/CustomMapPinsXamarin
First of all I would like to provide 2 links that helped me to understand the problem. Thank you guys.
Xamarin.Forms.Maps 2.3.4 custom MapRenderer disables everything and https://forums.xamarin.com/discussion/92565/android-ionmapreadycallback-forms-2-3-4
Latest Xamarin Maps broke OnElementPropertyChanged with VisibleRegion. They defined that MapRenderer now implements IOnMapReadyCallback and that broke somehow OnElementPropertyChanged (I didn't investigate how and why). As you can see in link I provided there are 2 methods you can go. To keep your renderer implementing IOnMapReadyCallback or not. When I kept IOnMapReadyCallback I started to get 2 pins - one of top another - our custom pin and regular pin. I didn't dig more how that happens and removed IOnMapReadyCallback. After that things became really simple because if you let Xamarin handle it and create NativeMap you can remove some code and make renderer simpler.
Before I post the code I also want to mention that when I fixed it the app started crashing with OutOfMemory exception and I found out that your pin image is 2000 pixels width. I changed it to 40. Below is the code:
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter//, IOnMapReadyCallback
{
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
}
bool isMapReady;
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (!isMapReady && (NativeMap != null))
{
NativeMap.SetInfoWindowAdapter(this);
NativeMap.InfoWindowClick += OnInfoWindowClick;
isMapReady = true;
}
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
NativeMap.Clear();
foreach (var pin in ((CustomMap)Element).CustomPins)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
NativeMap.AddMarker(marker);
}
isDrawn = true;
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
public Android.Views.View GetInfoContents(Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (customPin.Id == "Xamarin")
{
view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
}
else
{
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
}
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
return view;
}
return null;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in ((CustomMap)Element).CustomPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
}