System.NullReferenceException Xamarin Maps - xamarin

I'm getting a NullException when will display the map, but can not detect;
I tried to change the name of the maps, locazation, etc., but without success.
You may be able to identify where is my mistake, please help me!
public partial class LocationPage : ContentPage
{
public Clinica _clinica;
public LocationPage(Clinica clinica)
{
InitializeComponent();
Clinica = clinica;
SetupMap();
}
public Clinica Clinica
{
get
{
return _clinica;
}
set
{
_clinica = value;
}
}
protected override void OnAppearing()
{
base.OnAppearing();
// Typically, is preferable to call into the viewmodel for OnAppearing() logic to be performed,
// but we're not doing that in this case because we need to interact with the Xamarin.Forms.Map property on this Page.
// In the future, the Map type and it's properties may get more binding support, so that the map setup can be omitted from code-behind.
SetupMap();
}
void SetupMap()
{
if (Device.OS != TargetPlatform.WinPhone && Device.OS != TargetPlatform.Windows)
{
var pin = new Pin()
{
Type = PinType.Place,
Position = new Position(Clinica.Latitude, Clinica.Longitude),
Label = Clinica.Nome
};
clinicaMap.Pins.Clear();
clinicaMap.Pins.Add(pin);
clinicaMap.MoveToRegion(MapSpan.FromCenterAndRadius(pin.Position, Distance.FromMiles(10)));
}
}
}
}
LocationPage.xaml
<?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:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
x:Class="CartaoDeTodos.View.LocationPage">
<StackLayout>
<maps:Map x:Name="clinicaMap"
VerticalOptions="FillAndExpand"/>
</StackLayout>
</ContentPage>

Related

Xamarin iOS webview underlap behind navigation bar

I am using xamarin custom webview to load my page in app. But facing issue that title of webpage hides behind navigation bar . Or sometimes bottom of page not shown. I have tried adding scrollbar to my layout but still facing issue. Same works perfectly on android. Is it due to custom webview? I just want my webview to start below navigation bar and load completely according to device size.
my custom webview code :
public class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(CustomWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
Xaml Page :
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand">
<StackLayout>
<Label x:Name="type" Text="Loading..." FontSize="Medium"/>
</StackLayout>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<ScrollView Orientation="Vertical" FlowDirection="MatchParent" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand" Visual="Material" VerticalScrollBarVisibility="Always">
<OnPlatform x:TypeArguments="View">
<On Platform="Android">
<WebView x:Name="dashboard_android" HeightRequest="1000" WidthRequest="1000" />
</On>
<On Platform="iOS">
<local:CustomWebView x:Name="dashboard_ios" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand" WidthRequest="1000" HeightRequest="1000"/>
</On>
</OnPlatform>
</ScrollView>
</StackLayout>
</StackLayout>
code behind :
dashboard_android.Source = url;
dashboard_ios.Uri = url;
Following are solutions i have tried but no success
Solution 1 :
I have tried adding two properties, but no use
this.EdgesForExtendedLayout = UIRectEdge.None;
this.ExtendedLayoutIncludesOpaqueBars = false;
Solution 2 :
Tried enabling this unsafe area property , still no success
ios:Page.UseSafeArea="true"
Solution 3 :
Tried setting webview height on content size dynamically , but no success
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
// base.DidFinishNavigation(webView, navigation);
var wv = _webViewRenderer.Element as CustomWebView;
if (wv != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
wv.HeightRequest = (double)webView.Frame.Size.Height; // ScrollView.ContentSize.Height;
}
}
Updated Xaml Code :
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<local:CustomWebView x:Name="dashboard" HeightRequest="1000" WidthRequest="1000" />
</StackLayout>
Updated Code behind :
public partial class DashboardView : ContentPage
{
string url;
public DashboardView()
{
InitializeComponent();
url= ""; //adding url to load here
dashboard.Uri = url;
}
}
Custom WebView Renderer
[assembly: ExportRenderer(typeof(CustomWebView), typeof(MyCustomWebViewRenderer))]
namespace Report.iOS
{
public class MyCustomWebViewRenderer : ViewRenderer<CustomWebView, WKWebView>
{
WKWebView webView;
protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
webView = new WKWebView(Frame, new WKWebViewConfiguration());
webView.NavigationDelegate = new WebViewDelegate();
SetNativeControl(webView);
}
if (e.NewElement != null)
{
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Uri)));
}
}
}
public class WebViewDelegate : WKNavigationDelegate, INSUrlConnectionDataDelegate
{
string uname = null;
string pass = null;
public override async void DidReceiveAuthenticationChallenge(WKWebView webView, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
{
try
{
uname = Xamarin.Forms.Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Xamarin.Forms.Application.Current.Properties["Username"]) : null;
pass = await SecureStorage.GetAsync("Password");
}
catch (Exception ex)
{
}
completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, new NSUrlCredential(uname, pass, NSUrlCredentialPersistence.ForSession));
return;
}
}
}
Screenshot of webview screen :
Here i am loading this webpage(https://learn.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android). As you can see half of footer is hidden and i am not able to scroll it.
Screenshot of app
The reason for it quite simple actually you have added the WebView inside a scrollView which is, in turn, causing the issue webview has its own scroll so all you have to do is something like:
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<local:CustomWebView x:Name="dashboard" />
</StackLayout>
Also, you do not need the on the platform you can directly use the below and the custom renderer you have created.
The Height/Width request & layout options are not needed Webview by default will capture the whole viewport, You could actually even remove the StackLayouts, But that's on you.
Also, you might wanna read more about the webview
Good luck
Feel free to get back if you have queries
You can use latest WkWebViewRenderer:
public class MyCustomWebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
//this.LoadUrl("https://learn.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android");
this.NavigationDelegate = new WebViewDelegate();
}
}
In your code behind, you can directly set the source or set your binding:
dashboard.Source = "https://learn.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android";
Also, start from xamarin.forms 4.5+, xamarin use WKWebview as the default control in iOS and that means you no longer need a custom renderer if you use xamarin.forms 4.5+. Refer:
UIWebView Deprecation and App Store Rejection (ITMS-90809)
I was facing that issue just beacuse i was using custom renderer.
My solution code is as follows :
Xaml Code :
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<WebView x:Name="dashboard" HeightRequest="1000" WidthRequest="1000"/>
</StackLayout>
</ContentPage.Content>
Code Behind :
public partial class DashboardView : ContentPage
{
public DashboardView()
{
InitializeComponent();
dashboard.Source = "url";
}
}
Authentication Renderer iOS :
[assembly: ExportRenderer(typeof(WebView), typeof(Report.iOS.WebViewRenderer))]
namespace Report.iOS
{
class WebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
this.NavigationDelegate = new WebViewDelegate();
}
}
public class WebViewDelegate : WKNavigationDelegate, INSUrlConnectionDataDelegate
{
string uname = null;
string pass = null;
public override async void DidReceiveAuthenticationChallenge(WKWebView webView, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
{
try
{
uname = Xamarin.Forms.Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Xamarin.Forms.Application.Current.Properties["Username"]) : null;
pass = await SecureStorage.GetAsync("Password");
}
catch (Exception ex)
{
}
completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, new NSUrlCredential(uname, pass, NSUrlCredentialPersistence.ForSession));
return;
}
}
}
Authentication Renderer Android :
[assembly: ExportRenderer(typeof(WebView), typeof(AuthWebViewRenderer))]
namespace Report.Droid
{
public class AuthWebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
{
AuthWebViewClient _authWebClient = null;
public AuthWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (_authWebClient == null)
{
_authWebClient = new AuthWebViewClient();
}
Control.SetWebViewClient(_authWebClient);
}
}
public class AuthWebViewClient : WebViewClient
{
public AuthWebViewClient()
{
}
public override async void OnReceivedHttpAuthRequest(global::Android.Webkit.WebView view, HttpAuthHandler handler, string host, string realm)
{
string uname = null;
string pass = null;
try
{
uname = Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Application.Current.Properties["Username"]) : null;
pass = await SecureStorage.GetAsync("Password");
}
catch (Exception ex)
{
Log.Error("Apprise :", "Error Occurred while getting login credentials " + ex);
}
handler.Proceed(uname, pass);
}
}
}

How to create a Xamarin Tooltip in code-behind

I am testing using the following example. https://github.com/CrossGeeks/TooltipSample
The sample works fine, it even works with Labels (sample uses buttons, images and boxviews). The issue is in my main App I need to create the tooltips in code behind.
To test how to do it, in the very same solution (from that above example) I created a TestPage and made it my MainPage in App.xaml.cs. The XAML looks like this:
<?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="ToolTipSample.TestPage">
<ContentPage.Content>
<StackLayout
x:Name="mainLayout"
BackgroundColor="Yellow">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="Handle_Tapped"/>
</StackLayout.GestureRecognizers>
</StackLayout>
</ContentPage.Content>
The code-behind looks like this:
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using ToolTipSample.Effects;
namespace ToolTipSample
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestPage : ContentPage
{
public TestPage()
{
InitializeComponent();
var actionLabel = new Label
{
Text = "Show Tooltip",
WidthRequest = 150,
VerticalOptions = LayoutOptions.StartAndExpand,
HorizontalOptions = LayoutOptions.Center,
BackgroundColor = Color.Wheat
};
// Add tooltip to action label
TooltipEffect.SetPosition(actionLabel, TooltipPosition.Bottom);
TooltipEffect.SetBackgroundColor(actionLabel, Color.Silver);
TooltipEffect.SetTextColor(actionLabel, Color.Teal);
TooltipEffect.SetText(actionLabel, "This is the tooltip");
TooltipEffect.SetHasTooltip(actionLabel, true);
actionLabel.Effects.Add(Effect.Resolve($"CrossGeeks.{nameof(TooltipEffect)}"));
mainLayout.Children.Add(actionLabel);
}
void Handle_Tapped(object sender, System.EventArgs e)
{
foreach (var c in mainLayout.Children)
{
if (TooltipEffect.GetHasTooltip(c))
{
TooltipEffect.SetHasTooltip(c, false);
TooltipEffect.SetHasTooltip(c, true);
}
}
}
}
}
All other code unchanged.
When I tap the label, the tooltip appears as expected. But when I tap the background it does not disappear (like those created in XAML in the sample).
One other thing. If I tap twice it disappears.
Can anyone see what I am missing?
Thanks.
According to your description and code, you can delete the following line code to achieve your requirement.
actionLabel.Effects.Add(Effect.Resolve($"CrossGeeks.{nameof(TooltipEffect)}"));
You don't need to add effect for control when page load, because this effect will be added when you click this control by these code:
static void OnHasTooltipChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as View;
if (view == null)
{
return;
}
bool hasTooltip = (bool)newValue;
if (hasTooltip)
{
view.Effects.Add(new ControlTooltipEffect());
}
else
{
var toRemove = view.Effects.FirstOrDefault(e => e is ControlTooltipEffect);
if (toRemove != null)
{
view.Effects.Remove(toRemove);
}
}
}

Get start position of a listview scroll to end in xamarin forms wpf

I have worked on getting listview scroll position scroll to end in xamarin forms WPF application. I have tried below solution, it works in ios and android but unfortunately, it doesn't work in wpf application. Please suggest any idea to get scroll position of a listview end in xamarinforms WPF application.
Sample code you can find in below link
https://stackoverflow.com/questions/40373761/how-to-set-listview-to-start-showing-the-last-item-instead-in-xamarin-forms
If you're working with Xamarin Forms, you can create a control that extend from ListView and add methods for scrolling to top or bottom.
namespace YourAppName.Controls
{
public class CustomListView : ListView
{
public CustomListView() : this(ListViewCachingStrategy.RecycleElement)
{
}
public CustomListView(ListViewCachingStrategy cachingStrategy)
: base(cachingStrategy)
{
}
public void ScrollToFirst()
{
Device.BeginInvokeOnMainThread(() =>
{
try
{
if (ItemsSource != null && ItemsSource.Cast<object>().Count() > 0)
{
var firstItem = ItemsSource.Cast<object>().FirstOrDefault();
if (firstItem != null)
{
ScrollTo(firstItem, ScrollToPosition.Start, false);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
});
}
public void ScrollToLast()
{
try
{
if (ItemsSource != null && ItemsSource.Cast<object>().Count() > 0)
{
var lastItem = ItemsSource.Cast<object>().LastOrDefault();
if (lastItem != null)
{
ScrollTo(lastItem, ScrollToPosition.End, false);
}
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}
}
And on your xaml:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:YourAppName.Controls"
x:Class="YourAppName.Views.CustomListViewPage">
<controls:CustomListView
x:Name="customListView"
ItemsSource="{Binding Items}"
SeparatorVisibility="None"
SelectionMode="None"
HasUnevenRows="true">
<controls:CustomListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label
FontSize="Medium"
Text="{Binding TestText}" />
</ViewCell>
</DataTemplate>
</controls:CustomListView.ItemTemplate>
</controls:CustomListView>
</ContentPage>
And on the code behind you can do something like this:
namespace YourAppName.Views
public partial class CustomListViewPage : ContentPage
{
public CustomListViewPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
this.customListView.ScrollToLast();
}
}

Xamarin Forms - finding databound object from custom renderer while responding to a swipe

From an iOS swipe event, I am trying to figure out how to work my way back to the model databound to the ViewCell (model is my own Drive object, a simple POCO).
I am using a subclassed StackLayout ...
public class MainPageStackLayout : StackLayout { }
with a custom renderer...
[assembly: ExportRenderer(typeof(MainPageStackLayout), typeof(MainPageStackLayoutRenderer))]
namespace DriveLive.iOS
{
public class MainPageStackLayoutRenderer : VisualElementRenderer<StackLayout>
{
UISwipeGestureRecognizer swipeGestureRecognizer;
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
swipeGestureRecognizer = new UISwipeGestureRecognizer(() =>
{
//********************
Console.WriteLine("How to access the underlying model here?");
//********************
}) { Direction = UISwipeGestureRecognizerDirection.Left, NumberOfTouchesRequired = 1 };
if (e.NewElement == null)
{
if (swipeGestureRecognizer != null)
this.RemoveGestureRecognizer(swipeGestureRecognizer);
}
if (e.OldElement == null)
{
this.AddGestureRecognizer(swipeGestureRecognizer);
}
}
}
}
and the code that uses the MainPageStackLayout ...
<?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:DriveLive"
x:Class="DriveLive.Views.MainPage">
<ListView x:Name="___drives" HasUnevenRows="True">
<ListView.ItemTemplate />
</ListView>
</ContentPage>
C#
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
IDriveRespository repo = new DriveLive.Repository.Fakes.DriveRespository();
___drives.ItemsSource = repo.GetDrivesForUser(45); // returns a Drive list of objects
___drives.ItemTemplate = new DataTemplate(typeof(CustomViewCell));
___drives.SeparatorColor = Color.FromHex("#81C1B5");
}
}
public class CustomViewCell : ViewCell
{
bool _initialized = false;
StackLayout _cellStack;
public CustomViewCell()
{
_cellStack = new MainPageStackLayout()
{
Orientation = StackOrientation.Vertical,
HorizontalOptions = LayoutOptions.FillAndExpand
};
View = _cellStack;
var label = new Label() { FontAttributes = FontAttributes.Bold };
label.SetBinding(Label.TextProperty, new Binding("DestinationName"));
_cellStack.Children.Add(label);
}
}
From the handler for the UISwipeGestureRecognizer, how can I access the underlying Drive object which is databound to the ViewCell?
My issue is resolved by leveraging this piece of XForms.
ListView Interactivity - Context Actions
Credit goes to #skar's comment for pointing me in the right direction.

SelectedItem in TabbedPage.xaml does not work Xamarin.Form

Hey everyone Good Day I have tabbed created in xaml, I prefer xaml because I have login in xaml code. My Code
Tab.xaml
<TabbedPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FormsSample.Views;assembly=FormsSample"
x:Class="FormsSample.Views.LoginPage"
x:Name="TbPage">
<TabbedPage.Children>
<ContentPage x:Name="TbLog" Title="Login">
</ContentPage>
<ContentPage x:Name="TbSch" Title="Schedule">
</ContentPage>
<ContentPage x:Name="TbLis" Title="Customers">
</ContentPage>
</TabbedPage.Children>
</TabbedPage>
Tab.xaml.cs
namespace FormsSample.Views
{
public partial class LoginPage : TabbedPage
{
private readonly TabbedPage _tbPage;
private readonly ContentPage _tbList;
private readonly ContentPage _tbLogn;
public LoginPage()
{
InitializeComponent ();
_tbPage = this.FindByName<TabbedPage>("TbPage");
_tbList = this.FindByName<ContentPage>("TbLis");
_tbLogn = this.FindByName<ContentPage>("TbLog");
RunTime();
}
private void RunTime()
{
_tbPage.CurrentPage = _tbLogn;
if (_tbPage.SelectedItem == _tbPage.Children[2])
{
DisplayAlert("Tab", "Hello World", "OK");
}
}
}
}
changing to
_tbPage.SelectedItem == _tbList
Its similar nothing happen, How to solve this? thanks a lot.
This may not be fancy but it works for me :D
this.CurrentPageChanged += (o, e) =>
{
var i = this.Children.indexOf(this.CurrentPage);
if(i == 1 && UsrType == 2)
{
DisplayAlert("Admin", "Administrator Privilege required!", "OK");
this.CurrentPage = _tbLog;
}
};
I set SelectedItem to null first, before setting it to the page I wanted to show.

Resources