Xamarin.IOS Add a Splitview to Tabbed Application - c#

I am programming an Application on Xamarin.IOS. I have a Tabbed Layout as for my Main View and all the UIElements are set in the Storyboard. Now, what i need to do, because i want to implement IAds,
i want my Application to be a Split view, with the IAd View Controller as the Detail View, and my usual TabbedLayout as the main view.
So, yeah, my precise question would be:
As all my Layout is loaded from the Storyboard: How can i pass an instance of the View that gets loaded usually to my SplitViewController?
Any tips you can give, are helpful, i really need this for my work!!
To provide some Code:
I am trying to load my initial View (Which is the TabBarController) like this in the AppDelegate.cs:
SplitViewContoller splitView = new SplitViewContoller();
IADViewController iAdVC = new IADViewController (splitView);
Console.WriteLine ("Root" + Window.RootViewController);
Window.RootViewController = iAdVC;
That is working fine, except that the view that usually appears doesn't get loaded... The IAD shows up but the rest of the screen is an empty TabBarController.
Here is my SplitViewController:
public class SplitViewContoller : UISplitViewController
{
UIViewController masterView, detailView;
public SplitViewContoller () : base()
{
// create our master and detail views
masterView = new TabBarController();
detailView = new IADViewController (new TabBarController());
// create an array of controllers from them and then
// assign it to the controllers property
ViewControllers = new UIViewController[]
{ masterView, detailView }; // order is important
}
public override bool ShouldAutorotateToInterfaceOrientation
(UIInterfaceOrientation toInterfaceOrientation)
{
return true;
}
}
The View that i want to be at the bottom of my screen:
(See Monotouch.Dialog and iAds for details)
public partial class IADViewController : UIViewController
{
private UIViewController _anyVC;
private MonoTouch.iAd.ADBannerView _ad;
public IADViewController (UIViewController anyVC)
{
_anyVC = anyVC;
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
View.AddSubview (_anyVC.View);
Version version = new Version (MonoTouch.Constants.Version);
if (version > new Version (6,0))
{
try {
_ad = new MonoTouch.iAd.ADBannerView (MonoTouch.iAd.ADAdType.Banner);
_ad.Hidden = true;
_ad.FailedToReceiveAd += HandleFailedToReceiveAd;
_ad.AdLoaded += HandleAdLoaded;
View.BackgroundColor = UIColor.Clear;
_anyVC.View.Frame = View.Bounds;
View.AddSubview (_ad);
} catch {
}
} else {
Resize ();
}
}
public override void DidRotate (UIInterfaceOrientation fromInterfaceOrientation)
{
base.DidRotate (fromInterfaceOrientation);
Resize ();
}
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
Resize ();
}
void Resize ()
{
UIView.Animate (.25,
() => {
if (_ad !=null && _ad.Hidden == false) {
_anyVC.View.Frame = new RectangleF (0, 0, this.View.Bounds.Width, this.View.Bounds.Height - _ad.Frame.Height);
} else {
_anyVC.View.Frame = View.Bounds;
}
});
if(_ad!=null)
_ad.Frame = new RectangleF (0, _anyVC.View.Bounds.Height, this.View.Bounds.Width, _ad.Frame.Height);
}
void HandleAdLoaded (object sender, EventArgs e)
{
if (_ad == null)
return;
_ad.Hidden = false;
Resize ();
}
void HandleFailedToReceiveAd (object sender, AdErrorEventArgs e)
{
if (_ad == null)
return;
_ad.Hidden = true;
Resize ();
}
}

If anyone else need this, i solved it with the following lines of code now.
Storyboard = UIStoryboard.FromName ("MainStoryboard", null);
initialViewController = Storyboard.InstantiateInitialViewController () as UITabBarController;
// If you have defined a root view controller, set it here:
IADViewController iAdVc = new IADViewController (initialViewController);
Window.RootViewController = iAdVc;
// make the window visible
Window.MakeKeyAndVisible ();

Related

Adding UIImageView to UIAlertController

Goal: add an image above an UIAlertController's Title label by subclassing UIAlertController and adding new line characters, \n, to the title string to make space for the UIImageView
Desire
Current
As one can see, able to add the image to the UIAlertController successfully but the image is not being spacing/placed above the Title. It appears to be adding to the center of the alert. How to space the image correctly above the UIAlertController title?
Current code:
namespace XamarinFormsApp1.Extensions
{
public class AlertController : UIAlertController
{
private string originalTitle;
private string spaceAdjustedTitle;
private UIImageView imageView = null;
private CoreGraphics.CGSize previousImgViewSize
= CoreGraphics.CGSize.Empty;
public override UIAlertControllerStyle PreferredStyle
{
get
{
return UIAlertControllerStyle.Alert;
}
}
public override string Title
{
get
{
return originalTitle;
}
set
{
if (Title != spaceAdjustedTitle ||
string.IsNullOrEmpty(Title) ||
string.IsNullOrEmpty(spaceAdjustedTitle))
{
originalTitle = value;
}
}
}
public void setTitleImage(UIImage image)
{
if (this.imageView == null)
{
UIImageView imageView = new UIImageView(image);
this.View.AddSubview(imageView);
this.imageView = imageView;
return;
}
imageView.Image = image;
}
public override void ViewDidLayoutSubviews()
{
if (imageView == null)
{
base.ViewDidLayoutSubviews();
return;
}
// Adjust title if image size has changed
if (previousImgViewSize != imageView.Bounds.Size)
{
previousImgViewSize = imageView.Bounds.Size;
adjustTitle(imageView);
}
// Position `imageView`
var linesCount = newLinesCount(imageView);
var padding = Constants.Padding(PreferredStyle);
var x = View.Bounds.Width / 2.0;
var y = padding + linesCount * lineHeight / 2.0;
CoreGraphics.CGPoint cgPoint = imageView.Center;
cgPoint.X = (nfloat)x;
cgPoint.Y = (nfloat)y;
imageView.Center = cgPoint;
base.ViewDidLayoutSubviews();
}
private void adjustTitle(UIImageView imageView)
{
var linesCount = (int)newLinesCount(imageView);
var lines = Enumerable
.Range(1, linesCount)
.Select(i => "\n")
.Aggregate((c, n) => $"{c}{n}");
spaceAdjustedTitle = lines + (originalTitle ?? "");
Title = spaceAdjustedTitle;
}
private double newLinesCount(UIImageView imageView)
{
return Math.Ceiling(
imageView.Bounds.Height / lineHeight);
}
private float lineHeight
{
get
{
UIFontTextStyle style = this.PreferredStyle
== UIAlertControllerStyle.Alert
? UIFontTextStyle.Headline
: UIFontTextStyle.Callout;
return (float)UIFont
.GetPreferredFontForTextStyle(style)
.PointSize;
}
}
struct Constants
{
static float paddingAlert = 22;
static float paddingSheet = 11;
public static float Padding(UIAlertControllerStyle style)
{
return style == UIAlertControllerStyle.Alert
? Constants.paddingAlert
: Constants.paddingSheet;
}
}
}
}
Note: Credit to #stringCode for image and swift solution, see.
UIAlertViewController is not meant to be subclassed.
An extract from the documentation says:
You could still get the UI you desire by using a UIViewController with transparency on the View and a subview with the layout you desire.
You would need to also set these two properties: ModalTransitionStyle and ModalPresentationStyle to UIModalTransitionStyle.CrossDissolve and UIModalPresentationStyle.OverCurrentContext respectively if you want your custom UIAlertController to behaves the same as a UIAlertController
Update:
This is what I meant you could do:
In the Main.Storyboard drop a UIViewController and update the design as you wish. Following the image you posted above I created the UI as seen below:
That's an Image, 2 UILabels for the Title and Message and 3 buttons for the 3 different actions (Default, Destroy, Cancel). All these controls are inside a UIView with White background. For the example I called it ContentView
Adding the 3 button on the UI seems to be the easiest way to work with this and then hide/show them when you are about to present your alert. You could also create the buttons on the fly based on the actions you wanna show. This is up to you.
Create a ViewController Class, I called it NiceAlertController, and assign it to the ViewController in the Storyboard. Also, make sure to create back properties (Outlets) for all the UIControls (Label, Button, Image, etc) so you can access it from the ViewController class.
Here more information about how to work with iOS Storyboard on the designer
Now in your class you will need to add the code to make it work.
In your class to make the view transparent you will need to add this to your ViewDidLoad method:
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.View.BackgroundColor = UIColor.Clear.ColorWithAlpha(0.2f);
this.View.Opaque = false;
}
Also, we could mimic the way UIAlertControllers are created and create our method like that one:
public static NiceAlertController Create(string title, string message, UIImage image = null)
{
//New instance of your ViewController UI
var storyboard = UIStoryboard.FromName("Main", NSBundle.MainBundle);
var alertController = storyboard.InstantiateViewController(nameof(NiceAlertController)) as NiceAlertController;
//Using the same transition and presentation style as the UIAlertViewController
alertController.ModalTransitionStyle = UIModalTransitionStyle.CrossDissolve;
alertController.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
//This assumes your properties are called Title, Message and Image
alertController.Title = title;
alertController.Message = message;
alertController.Image = image;
return alertController;
}
The 3 properties used above (Title, Message and Image) looks like this:
public new string Title { get; private set; }
public string Message { get; private set; }
public UIImage Image { get; private set; }
Why these properties? because by the time you create the class the Controls on the view are not yet available. They will be available only after the View is loaded. This is why we will need to add other changes like the one below.
Here we are setting the values to the Controls on the UI
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
this.titleLabel.Text = Title;
this.messageLabel.Text = Message;
//If you don't set an image while Create, it will use the default image you set on the Designer.
if (Image != null)
{
this.imageView.Image = Image;
}
}
Now from any other ViewController you can call this ViewController as you would call an AlertViewController:
private void ShowMyAlertController()
{
var alert = NiceAlertController.Create("Hello", "My nice alert controller");
this.PresentViewController(alert, true, null);
}
And it should look like this:
To handle the Actions (What to do when the buttons are tapped) you could create specific methods like:
public void AddDefaultAction(string title, Action action)
{
//Logic here
}
public void AddCancelAction(string title, Action action)
{
//Logic here
}
public void AddDestructiveAction(string title, Action action)
{
//Logic here
}
Hope this gives you the idea of how to create custom UIViewcontroller and make it look like a UIAlertViewController.
Hope this helps.-

How to access button click inside listview datatemplate in Xamarin.Forms?

I have listview that have two views manipulated dynamically label and button, and I am trying to access button click to go to the details page on button click.
below is my custom ViewCell
protected override async void OnAppearing()
{
listClass.ItemsSource = list;
listClass.ItemTemplate = new DataTemplate(typeof(ItemTemplateViewCell ));
}
public class ItemTemplateViewCell : ViewCell
{
Label NameLbl = new Label();
StackLayout sLayout = new StackLayout ();
Button btnViewcell = new Button {Text = "Show class details"};
public ItemTemplateViewCell()
{
NameLbl.SetBinding(Label.TextProperty, "Name");
sLayout.Children.Add(NameLbl);
btnViewcell.Clicked += (s, e) =>
{
// Navigation.PushAsync(new Home()); //I can not using this line
// does not exist in the current context, why cant i navigate to
// another page from inside datatemplate in List view
};
sLayout.Children.Add(btnViewcell);
this.View = sLayout;
}
}
You can pass the Navication to ViewCell through constructor:
public class ItemTemplateViewCell : ViewCell
{
// Navigation Mermber
INavigation MyNavigation;
Label NameLbl = new Label();
StackLayout sLayout = new StackLayout ();
Button btnViewcell = new Button {Text = "Show class details"};
public ItemTemplateViewCell(INavigation navigation)
{
MyNavigation = navigation;
NameLbl.SetBinding(Label.TextProperty, "Name");
sLayout.Children.Add(NameLbl);
btnViewcell.Clicked += ButtonShowDetails_Clicked;
sLayout.Children.Add(btnViewcell);
this.View = sLayout;
}
private void ButtonShowDetails_Clicked(object sender, EventArgs e)
{
MyNavigation.PushAsync(new Home());
}
}
Then pass the Navication through the delegate Function
protected override async void OnAppearing()
{
listClass.ItemsSource = list;
listClass.ItemTemplate = new DataTemplate(Function) ;
}
public object Function()
{
return new ItemTemplateViewCell (Navigation);
}
Then you can access the Navigation object in ViewCell
Simply you are able to use CommandParameterProperty with binding :
Example:
ListViewClass.ItemTemplate = new DataTemplate(() =>
{
var GoHomeButton = new Button { Text = "Go Home", HorizontalOptions = LayoutOptions.StartAndExpand };
GoHomeButton.SetBinding(Button.CommandParameterProperty, new Binding("Name"));
GoHomeButton.Clicked += Gohome;
//return new view cell
}
private void Gohome(object sender, EventArgs e)
{
Navigation.PushAsync(new Home());
}
For more info check the following Link:
http://www.c-sharpcorner.com/blogs/handling-child-control-event-in-listview-using-xamarinforms1
What I think is going wrong is that you are performing an action immediately in the initialization of the class. You should split it up so like below. Another issue is that you do initialization of variables outside of the methods. This might go okay, but I prefer the below code:
public class ItemTemplateViewCell : ViewCell
{
Label nameLbl;
StackLayout sLayout;
Button btnViewcell;
public ItemTemplateViewCell()
{
nameLbl = new Label()
sLayout = new StackLayout ()
btnViewcell = new Button {Text = "Show class details"};
NameLbl.SetBinding(Label.TextProperty, "Name");
sLayout.Children.Add(NameLbl);
btnViewcell.Clicked += OnButtonClicked;
sLayout.Children.Add(btnViewcell);
this.View = sLayout;
}
void OnButtonClicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Home());
}
}
I think this should work in your case but I can't be sure. I don't think what you posted is one on one your code since I don't see any initialization of s and e in your code and as mentioned in the comments the comment in your code would cause issues if you really put it as in the question. Also you don't share the code of the Home class. Something might be wrong at that end.
Navigation is not available inside of a ViewCell, this means you can't access it right from your ViewCell. But this should work:
App.Current.MainPage.Navigation.PushAsync(new Home());
The whole code:
protected override async void OnAppearing()
{
listClass.ItemsSource = list;
listClass.ItemTemplate = new DataTemplate(typeof(ItemTemplateViewCell ));
}
public class ItemTemplateViewCell : ViewCell
{
Label NameLbl = new Label();
StackLayout sLayout = new StackLayout ();
Button btnViewcell = new Button {Text = "Show class details"};
public ItemTemplateViewCell()
{
NameLbl.SetBinding(Label.TextProperty, "Name");
sLayout.Children.Add(NameLbl);
btnViewcell.Clicked += (s, e) =>
{
App.Current.MainPage.Navigation.PushAsync(new Home());
};
sLayout.Children.Add(btnViewcell);
this.View = sLayout;
}
}

How to access method from view inside a Xamarin Forms custom renderer?

I have the following code:
public partial class PhrasesFrameRendererClass : Frame
{
.....
void getRandomWords() {
// more code here that involves getting random numbers
// and updating a grid's bindingcontext
}
}
In my custom renderer I want to be able to call the getRandomWords on swipe left gesture like below:
public class PhraseFrameCustomRenderer : FrameRenderer
{
UISwipeGestureRecognizer leftSwipeGestureRecognizer;
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
leftSwipeGestureRecognizer = new UISwipeGestureRecognizer();
leftSwipeGestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;
leftSwipeGestureRecognizer.NumberOfTouchesRequired = 1;
leftSwipeGestureRecognizer.AddTarget((obj) =>
{
// Call getRandomWords() here
});
}
}
Is this possible? Any ideas on how this could be done?
base.OnElementChanged(e);
leftSwipeGestureRecognizer = new UISwipeGestureRecognizer();
leftSwipeGestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;
leftSwipeGestureRecognizer.NumberOfTouchesRequired = 1;
leftSwipeGestureRecognizer.AddTarget((obj) =>
{
// Call getRandomWords() here
var frame = Element as PhrasesFrameRendererClass ;
if(frame!=null){
frame.getRandomWords();
}
});
You can create a BindableProperty of type Command in your custom frame class, call that Command from your renderer and bind your ViewModel's getRandomWords method as a Command
//Your custom control in your PCL project
public partial class PhrasesFrameRendererClass : Frame
{
public static readonly BindableProperty SwipeLeftCommandProperty =
BindableProperty.Create(nameof(SwipeLeftCommand), typeof(ICommand), typeof(PhrasesFrameRendererClass ), null);
public ICommand SwipeLeftCommand
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}
//Your custom control renderer
public class PhraseFrameCustomRenderer : FrameRenderer
{
UISwipeGestureRecognizer leftSwipeGestureRecognizer;
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
leftSwipeGestureRecognizer = new UISwipeGestureRecognizer();
leftSwipeGestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;
leftSwipeGestureRecognizer.NumberOfTouchesRequired = 1;
leftSwipeGestureRecognizer.AddTarget((obj) =>
{
var myFrame = Element as PhrasesFrameRendererClassl
if(myFrame != null){
if(myFrame.SwipeLeftCommand != null && myFrame.SwipeLeftCommand.CanExecute()){
myFrame.SwipeLeftCommand.Execute();
}
}
});
}
}
//Your ViewModel
public class PhrasesViewModel{
public Command GetRandomWordsCommand {get;set;}
public PhrasesViewModel(){
GetRandomWordsCommand = new Command(ExecuteGetRandomWords);
}
private void ExecuteGetRandomWords(){
//Your method goes here
}
}
//Your XAML
<yourControls:PhrasesFrameRendererClass SwipeLeftCommand="{Binding GetRandomWordsCommand }"/>
It may seem more complicated this way, but using commands allows you to separate your application code (Such as getting random phrases) from your rendering code

Edit text in Listview Custom Adapter Loses its position while scrolling? - c# - Xamarin.Android

When we enter value in row 1 the value entered in row 1 is appearing back in row 6 when we scroll to the row 6. Please see the below code and advice.
namespace Kites
{
public class Marks
{
// add any if you need more
public string StudentName { get; set; }
public string MarksScored { get; set; }
}
public class TEXTCHECK
{
public int POS { get; set; }
public string Value { get; set; }
}
public class MarksListViewAdapter : BaseAdapter<Marks>
{
private List<Marks> mstuduentmarks;
private List<TEXTCHECK> abc = new List<TEXTCHECK>();
private Context mcontext;
public MarksListViewAdapter (Context context, List<Marks> stud)
{
mstuduentmarks = stud;
mcontext = context;
}
public override int Count
{
get
{
return mstuduentmarks.Count;
// return mattendence.Count;
}
}
public override long GetItemId (int position)
{
return position;
}
public override Marks this[int position]
{
get
{
return mstuduentmarks [position];
// return mattendence [position];
}
}
class ViewHolder : Java.Lang.Object
{
public EditText comsevin;
public TextView namenmn;
}
public override View GetView (int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
View view = convertView;
if (view == null) // otherwise create a new one
{
view = LayoutInflater.From(mcontext).Inflate(Resource.Layout.listview_Marks, null, false);
holder = new ViewHolder();
holder.comsevin = view.FindViewById<EditText>(Resource.Id.editTextTeacherMarks);
holder.namenmn = view.FindViewById<TextView>(Resource.Id.textStudentNameTeacherMarks);
holder.namenmn.Tag = position;
view.Tag = holder;
}
else
{
holder = (ViewHolder)view.Tag;
}
holder.namenmn.Text = mstuduentmarks[position].StudentName;
int pos = (int)holder.namenmn.Tag;
holder.comsevin.TextChanged += (sender, e) =>
{
abc[pos].Value = holder.comsevin.Text;
};
//TextView txtStudent =
//txtStudent.Text = mstuduentmarks[position].StudentName;
//txtMarks.FocusChange += (object sender, View.FocusChangeEventArgs e) =>
//{
// //txtMarks.RequestFocusFromTouch ();
// mstuduentmarks[position].MarksScored = txtMarks.Text;
//};
holder.comsevin.BeforeTextChanged += (sender, e) =>
{
abc.Add(new TEXTCHECK { POS = position, Value = mstuduentmarks[position].MarksScored });
};
holder.comsevin.AfterTextChanged += (sender, e) =>
{
int a = abc[pos].POS;
mstuduentmarks[pos].MarksScored = abc[pos].Value;
};
//txtMarks.Tag = position;
//txtMarks.TextChanged += TxtMarks_TextChanged;
return view;
}
//void TxtMarks_TextChanged (object sender, Android.Text.TextChangedEventArgs e)
//{
// EditText txtMarks = (EditText)sender;
// //var position = (int)txtMarks.Tag;
//}
}
}
When we enter value in row 1 the value entered in row 1 is appearing back in row 6 when we scroll to the row 6. Please see the below code and advice.
As a rule of thumb, when experiencing lists that don't reflect the dataset (experiencing item repetition for example) in listview / recyclerview it means that you're either using dirty views which were previously used and then uncorrectly Re-Bound, or simply using wrong positions during bind
I see where you are getting it wrong:
if (view == null) // otherwise create a new one
{
view = LayoutInflater.From(mcontext).Inflate(Resource.Layout.listview_Marks, null, false);
holder = new ViewHolder();
holder.comsevin = view.FindViewById<EditText>(Resource.Id.editTextTeacherMarks);
holder.namenmn = view.FindViewById<TextView>(Resource.Id.textStudentNameTeacherMarks);
holder.namenmn.Tag = position;//<------------here!!!
view.Tag = holder;
}
TLDR Don't save positions this way.
Whats happening: this instance of your view is being reused by listView, meaning that sometimes (many times) if (view == null) will be false and this means Tag property will not be updated for row 6 (or any other calls that will use recycled Views) and you are in fact using a dirty value.
You are then trying to use the Tag property as position, but forgetting this tag is already dirty if the view was recycled
int pos = (int)holder.namenmn.Tag;
holder.comsevin.TextChanged += (sender, e) =>
{
abc[pos].Value = holder.comsevin.Text;
};
Since you have access to the position in this method call you should use it directly
take a look at this guide from Java Code geeks even though it's in Java you will be able to see a good implementation of the old ViewHolder/ListView pattern.
Hope this helps

How to receive feedback from classRenderer

I needed to implement with a custom-renderer a map in my project developed with Xamarin Forms, here is how I did it:
public class MyMap : Map
{
public MyMap ():base(){ }
}
MapView(Xamarin Forms):
public class MapView:ContentPage
{
List<Filiale> list=jM.ReadData ();
MyMap myMap = new MyMap (//I'd like to pass list here);
var stack = new StackLayout ();
myMap.VerticalOptions = LayoutOptions.FillAndExpand;
myMap.HorizontalOptions = LayoutOptions.FillAndExpand;
stack.Children.Add (myMap);
Content = stack;
}
MapRenderer (Xamarin iOS):
[assembly: ExportRenderer (typeof (MyMap), typeof (MapiOS))]
namespace App.iOS
{
public class MapiOS : MapRenderer
{
private MKMapView NativeMap { get { return (this.NativeView as MapRenderer).Control as MKMapView; } }
public MapiOS ():base()
{
}
protected override void OnElementChanged (ElementChangedEventArgs<View> e)
{
base.OnElementChanged (e);
MyMapDelegate myMapDelegate = new MyMapDelegate ();
NativeMap.Delegate = myMapDelegate;
NativeMap.AddAnnotation(new MKPointAnnotation (){
Title=list[0].nome,
Coordinate = new CLLocationCoordinate2D (42.364260, -71.120824)
});
}
}
In the myMapDelegate class I also handled the click on a button displayed from clicking on the Pins like this:
public override void CalloutAccessoryControlTapped (MKMapView mapView, MKAnnotationView view, UIControl control){
//Call new Page
}
Now when this button is clicked I'd like to get back to Xamarin Forms and create a new page with it. How should I do it? Also How can I pass some object when I create the MyMap object?
Change your MyMap implementation to this
public class MyMap : Map
{
public static readonly BindableProperty LocationsProperty = BindableProperty.Create<MyMap, List<string>>(x => x.Locations, new List<string>());
public static readonly BindableProperty PinTappedCommandProperty = BindableProperty.Create<MyMap, Command>(x=>x.PinTapped, null);
public MyMap(List<string> locations)
{
Locations = locations;
PinTapped = new Command(async (x) =>
{
await Navigation.PopModalAsync();
await Navigation.PushAsync(SomeNewPage(x));
});
}
public List<string> Locations
{
get { return (List<string>)GetValue(LocationsProperty); }
set { SetValue(LocationsProperty, value); }
}
public Command PinTapped
{
get { return (Command) GetValue(PinTappedCommandProperty); }
set { SetValue(PinTappedCommandProperty, value);}
}
}
Now you can access Locations from your MapRenderer by changing it slightly:
public class MapiOS : ViewRenderer<MyMap, MKMapView>
{
protected override void OnElementChanged (ElementChangedEventArgs<MyMap> e)
{
base.OnElementChanged (e);
var map = e.NewElement; // Remember to check for null
var locations = map.Locations;
// Do what you want with locations
var cmd = Map.PinTapped; // Send this along to MyMapDelegate
var nativeMap = new MKMapView(); // Initiate with relevant parameters
SetNativeControl(nativeMap)
MyMapDelegate myMapDelegate = new MyMapDelegate (cmd); // Change constructor here
nativeMap.Delegate = myMapDelegate;
nativeMap.AddAnnotation(new MKPointAnnotation (){
Title=list[0].nome,
Coordinate = new CLLocationCoordinate2D (42.364260, -71.120824)
});
}
I have assumed that you have shown the map as a modal inside a NavigationPage or similar.
You now have the Command, so bring it along to your MyMapDelegate and use
x.PinTapped.Execute("YourParameter");

Categories

Resources