Use custom renderer on only one Entry control - xamarin.forms

In my Xamarin.Forms app, I have a custom renderer:
[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))]
namespace MyApp.Controls
{
public class CustomEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (this.Control != null)
{
Control.Style = (Windows.UI.Xaml.Style)App.Current.Resources["CustomTextBoxStyle"];
}
}
}
}
But I only want to use it on 1 of my Entry controls, not all of them. How can I accomplish this?

create a custom control MyEntry that inherits from Entry
then in your renderer, specify that it only applies to type MyEntry
[assembly: ExportRenderer(typeof(MyEntry), typeof(CustomEntryRenderer))]

Related

Find when a page finished loading in xamarin.forms prism

I am trying to create an optimized Event Tracker for my Xamarin.forms prism application. I would like to know whether there is any method to find out when the page load is finished. If someone could help me, please save my time.
You can implement page loading methods for different platforms through custom renderer.
For iOS, override the viewDidLoad method, ViewDidLoad(Called after the controller's view is loaded into memory.)
[assembly:ExportRenderer (typeof(ContentPage), typeof(MyRenderer))]
namespace Demo.iOS
{
public class MyRenderer : PageRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
}
}
}
For Android, rewrite the OnAttachedToWindow method, OnAttachedToWindow(This is called when the view is attached to a window.)
[assembly: ExportRenderer(typeof(ContentPage), typeof(MyRenderer))]
namespace Demo.Droid
{
public class MyRenderer : PageRenderer
{
public MyPageRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
}
}
}

Custom MapBox Android Renderer - crash when change tabs

I am trying to create a MapBox Renderer for Android in Xamarin Forms using the naxam library.
The map is displaying well as a content of a view in a tab, however when I change tab several times it crashes on the android emulator. I suppose the problem comes from the renderer but can't figure out where.
Here is a part of my code :
public class MapViewRenderer : ViewRenderer<ContentView, Android.Views.View>,
IOnMapReadyCallback, Com.Mapbox.Mapboxsdk.Maps.Style.IOnStyleLoaded
{
private MapView _mapView = null;
private MapboxMap _mapBox = null;
public MapViewRenderer(Context context) : base(context)
{
Mapbox.GetInstance(Context, "pk.###");
}
public void OnMapReady(MapboxMap p0)
{
_mapBox = p0;
[...]
}
public void OnStyleLoaded(Com.Mapbox.Mapboxsdk.Maps.Style p0)
{
[...]
}
protected override void OnElementChanged(ElementChangedEventArgs<ContentView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
_mapView = new MapView(Context);
_mapView.GetMapAsync(this);
view.Content = _mapView.ToView();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_mapView.Dispose();
}
base.Dispose(disposing);
}
}
All the examples I read on the net are about creating the MapView in the main activity with OnCreate, OnStart, OnResume... I haven't found any about creating a map in a custom render.
Please help.
EDIT :
---------------------------------SOLUTION---------------------------------
The problem was fixed using the code below in the custom renderer. In addition the renderer uses a Mapview instance which has been moved inside the main activity following ToolmakerSteve's remark.
MapViewRenderer.cs :
public class MapViewRenderer : ViewRenderer<ContentView, Android.Views.View>
{
public MapViewRenderer(Context context) : base(context)
{}
protected override void OnElementChanged(ElementChangedEventArgs<ContentView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var view = e.NewElement as Views.Map;
if (Control == null)
{
SetNativeControl(MainActivity.MainActivityInstance.MapView);
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
MainActivity.MainActivityInstance.MapView.RemoveFromParent();
}
base.Dispose(disposing);
}
}
MainActivity.cs :
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IOnMapReadyCallback, Style.IOnStyleLoaded
{
public MapView MapView { get; private set; } = null;
public MapboxMap MapboxMap { get; private set; } = null;
public static MainActivity MainActivityInstance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
[...]
MainActivityInstance = this;
Mapbox.GetInstance(this, "pk.###");
MapView = new MapView(this);
MapView.GetMapAsync(this);
MapView.OnCreate(savedInstanceState);
}
protected override void OnStart()
{
base.OnStart();
MapView.OnStart();
[...]
}
protected override void OnResume()
{
base.OnResume();
MapView.OnResume();
}
protected override void OnPause()
{
MapView.OnPause();
base.OnPause();
}
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
MapView.OnSaveInstanceState(outState);
}
protected override void OnStop()
{
base.OnStop();
MapView.OnStop();
}
protected override void OnDestroy()
{
MapView.OnDestroy();
base.OnDestroy();
}
public override void OnLowMemory()
{
base.OnLowMemory();
MapView.OnLowMemory();
}
public void OnMapReady(MapboxMap p0)
{
MapboxMap = p0;
[...]
}
public void OnStyleLoaded(Com.Mapbox.Mapboxsdk.Maps.Style p0)
{
[...]
}
}
Wrap the contents of each method in try-catch, and write to Debug output any exception (hopefully instead of crashing).
You will probably need to find out exactly what causes the crash, for anyone to help you.
Do this by adding Debug.WriteLine statements throughout the custom renderer.
I also recommend verifying that it really is the custom renderer that causes the problem.
Do this by commenting out most of the custom code. So that it displays as an empty view.
You need to add code to the MainActivity methods. This won't fix this crash, but it will avoid crashing later, for example when app goes into background.
Naxam library install guide says:
The MapView contains its own lifecycle methods for managing Android's OpenGL lifecycle, which must be called directly from the containing Activity. In order for your app to correctly call the MapView's lifecycle methods, you must override the following lifecycle methods in the Activity that contains the MapView and call the respective MapView method.
Android code from that link:
// Set this from your custom renderer.
public MapView mapView;
override fun onStart() {
super.onStart()
mapView?.onStart()
}
override fun onStop() {
super.onStop()
mapView?.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
mapView?.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView?.onDestroy()
}
Translate that into C# equivalent.
In your custom renderer, set the activities mapView variable:
Xamarin Essentials.Platform.CurrentActivity.mapView = ...;
You should also set that variable to null:
in OnElementChanged / e.newElement == null
in Dispose.
In my experience, on Android, OpenGL views don't like to be hidden - which is what happens when switching to different tab. (MapBox uses an OpenGL view.)
Unfortunately the result is a "hard crash" - won't be caught by try/catch. Won't give useful information about why it crashed.
If this is the cause, none of the above will fix the problem.
You might have to do custom logic that disposes the map when the tab is hidden, recreates it when the tab comes back.
See https://stackoverflow.com/a/52186885/199364, for the event you need to add code to.

How to access ViewWillAppear/ViewDidDisappear in ViewRenderer<HybridWebView, WkWebViewRenderer>?

I'm trying to port an Android existing custom ViewRenderer to iOS.
I'm using version 4.8 of Xamarin.Forms.
Specifically, I need to match Android's OnAttachedToWindow & OnDetachedFromWindow.
The Android Code is like the following:
public partial class HybridWebViewRenderer : ViewRenderer<HybridWebView, Android.Webkit.WebView>
{
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
...
}
protected override void OnDetachedFromWindow()
{
base.OnDetachedFromWindow();
...
}
}
According to this link, I would need to override ViewWillAppear which is the iOS equivalent to OnAttachedToWindow.
The custom Renderer for iOS is the following:
public partial class HybridWebViewRenderer : ViewRenderer<HybridWebView, WkWebViewRenderer>, IWKScriptMessageHandler
{
}
But the overrides are not available.
How can I do that?
ViewWillAppear and ViewWillDisappear are the life cycle method of UIViewController.So you could create the custom renderer of ContentPage
in the ContentPage that contains the WebView
using Foundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using App1;
using App1.iOS;
[assembly:ExportRenderer(typeof(MainPage),typeof(MyPageRenderer))]
namespace App1.iOS
{
public class MyPageRenderer:PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
MessagingCenter.Send<Object,UIViewController>(this,"ViewWillAppear",this); // send message when the method been called .
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
MessagingCenter.Send<Object, UIViewController>(this, "ViewWillDisappear", this);
}
}
}
in WebViewRenderer
Subscribe the message in the constructor .
MessagingCenter.Subscribe<Object, UIViewController > (this, "ViewWillDisappear",(arg,controller)=> {
// controller is the current UIViewController
});

Add Native View in Xamarin.Forms

i would like to add a TextInputEdit text in my Xamarin.Forms project, is possible to add in code or in XAML? i need to add reference to Mono.Android dll?
You can use Customizing an Entry to realize it .
Creating the Custom Entry Control :
public class MyEntry : Entry
{
}
Consuming the Custom Control:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
...
<local:MyEntry Text="In Shared Code" />
...
</ContentPage>
Creating the Custom Renderer on Android :
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]
namespace CustomRenderer.Android
{
class MyEntryRenderer : EntryRenderer
{
public MyEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SetBackgroundColor(global::Android.Graphics.Color.LightGreen);
}
}
}
}
Here is the official sample for reference.

Overriding OnDisappearing in custom Renderer Xamarin. Forms

I want to call a base.Dispose() on the OnDisappearing() from my Android project in Xamarin.Forms.
I have done something like this :-
namespace Project.Mobile.Droid
{
[assembly: ExportRenderer(typeof(ItemListPage), typeof(DisposeRenderer))]
class DisposeRenderer : ViewRenderer
{
public DisposeRenderer() { }
}
}
How can I go ahead with this?
Iam new to Xamarin.Forms, so it would be a great help if someone helps me on this.
As long as your ItemListPage implements IDisposable, you could try the following :-
using System;
using DisposableControlSample;
using DisposableControlSample.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(DisposablePage), typeof(DisposablePageRenderer))]
namespace DisposableControlSample.Droid
{
public class DisposablePageRenderer : PageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
e.NewElement.Disappearing += (sender, args) =>
{
var disposablePage = sender as IDisposable;
if (disposablePage != null)
{
disposablePage.Dispose();
}
};
}
}
}

Resources