Unable to get the trigger button event call back from Web view for Xamarin using hybridWebView - xamarin

I have my mobile application build using Xamarin forms, which consist of a webView loaded into my application. The WebView Consist of buttons and icons being loaded. I have a requirement to trigger the button event Listener , so that I can do the corresponding functionality. Can anyone help me in how to get the button events being called in Xamarin forms. I have used HybridWebView as mentioned in the documents and sample ,but nothing seems to be working for me. Below is the code I have used for the same.
code:
HybridWebViewRenderer
using System;
using Android.Content;
using TestProject.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Vinspector.HybridWebView), typeof(HybridWebViewRenderer))]
namespace TestProject.Droid
{
public class HybridWebViewRenderer : WebViewRenderer
{
const string JavascriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);};";
Context _context;
public HybridWebViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
Console.WriteLine("print inside control is null!!!");
var webView = new Android.Webkit.WebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.Settings.DatabaseEnabled = true;
webView.Settings.DomStorageEnabled = true;
SetNativeControl(webView);
}
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
Control.Settings.JavaScriptEnabled = true;
Control.Settings.DatabaseEnabled = true;
Control.Settings.DomStorageEnabled = true;
Control.SetWebViewClient(new JavascriptWebViewClient("javascript:(function getCloseCa(){var btns = document.getElementById('dvCloseBtn');})()"));
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl(“www.google.com”);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((HybridWebView)Element).Cleanup();
}
base.Dispose(disposing);
}
}
}
Code For JavascriptWebViewClient
namespace TestProject.Droid
{
public class JavascriptWebViewClient : WebViewClient
{
string _javascript;
public JavascriptWebViewClient(string javascript)
{
_javascript = javascript;
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
view.EvaluateJavascript(_javascript, null);
}
else {
view.LoadUrl(_javascript);
}
}
}
}
Code For JsBridge
namespace TestProject.Droid
{
public class JSBridge : Java.Lang.Object
{
readonly WeakReference<HybridWebViewRenderer> hybridWebViewRenderer;
public JSBridge(HybridWebViewRenderer hybridRenderer)
{
hybridWebViewRenderer = new WeakReference<HybridWebViewRenderer>(hybridRenderer);
}
[JavascriptInterface]
[Export("invokeAction")]
public void InvokeAction(string data)
{
HybridWebViewRenderer hybridRenderer;
if (hybridWebViewRenderer != null && hybridWebViewRenderer.TryGetTarget(out hybridRenderer))
{
((HybridWebView)hybridRenderer.Element).InvokeAction(data);
}
}
}
}
WebPage.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:local="clr-namespace:TestProject."
x:Class="TestProject..WebPage">
<StackLayout>
<ProgressBar Progress="0.2"
HorizontalOptions="FillAndExpand"
x:Name="progress"
IsVisible="True"/>
<local:HybridWebView
x:Name="hybridWebView"
HeightRequest="1000"
WidthRequest="1000"
Navigating="OnNavigating"
Navigated="OnNavigated"
VerticalOptions="FillAndExpand"
>
</local:HybridWebView>
</StackLayout>
</ContentPage>
WebPage.Xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Vinspector.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Newtonsoft.Json;
using TestProject.Models;
namespace TestProject
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class WebPage : ContentPage
{
public WebPage()
{
InitializeComponent();
hybridWebView.RegisterAction(data => Console.WriteLine("value of data is" + data));
}
protected async override void OnAppearing()
{
base.OnAppearing();
await progress.ProgressTo(0.9, 900, Easing.SpringIn);
}
protected void OnNavigating(object sender, WebNavigatingEventArgs e)
{
progress.IsVisible = true;
Console.WriteLine(" Result OnNavigating....");
}
protected void OnNavigated(object sender, WebNavigatedEventArgs e)
{
progress.IsVisible = false;
Console.WriteLine(" Result OnNavigated....");
var retorno = hybridWebView.EvaluateJavaScriptAsync("common.GetCurrentUserID();");
covertAsync(retorno);
}
private async Task covertAsync(Task<string> retorno)
{
string userID = await retorno;
string AccessToken = LoadApplicationProperty<string>("access_token");
Console.WriteLine(" Result UserID...." + userID + " " + AccessToken);
}
private T LoadApplicationProperty<T>(string key)
{
return (T)Xamarin.Forms.Application.Current.Properties[key];
}
}
}

You can use the javascript function like the below code to access the buttons and events clicked from webview:
using System;
using Android.Content;
using TestProject.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Vinspector.HybridWebView), typeof(HybridWebViewRenderer))]
namespace TestProject.Droid
{
public class HybridWebViewRenderer : WebViewRenderer
{
const string JavascriptFunction= "document.getElementById('btnId').onclick= function(data){jsBridge.submissionAction(data);}";`
Context _context;
public HybridWebViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
Console.WriteLine("print inside control is null!!!");
var webView = new Android.Webkit.WebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.Settings.DatabaseEnabled = true;
webView.Settings.DomStorageEnabled = true;
SetNativeControl(webView);
}
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
Control.Settings.JavaScriptEnabled = true;
Control.Settings.DatabaseEnabled = true;
Control.Settings.DomStorageEnabled = true;
webView.SetWebViewClient(new `JavascriptWebViewClient(this,string.Format("javascript: {0}", JavascriptFunction)));`
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl(“www.google.com”);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((HybridWebView)Element).Cleanup();
}
base.Dispose(disposing);
}
}
}

Related

Setting current url in a custom xamarin forms web view

I'm starting with Xamarin.Forms and what I'm trying to do is simply setting a CurrentUrl property on a custom Webview in Xamarin.Forms
In other words: When OnPageFinished method is called, I need to set the CurrentUrl property of MyWebView to the new Url.
Anyone have an idea?
Here's my main Webview:
public class MyWebView: Xamarin.Forms.WebView
{
public static readonly BindableProperty UrlProperty = BindableProperty.Create(
propertyName: "CurrentUrl",
returnType: typeof(string),
declaringType: typeof(MyWebView),
defaultValue: default(string));
public string CurrentUrl
{
get { return (string)GetValue(UrlProperty); }
set { SetValue(UrlProperty, value); }
}
}
And here's my Renderer in Project.Droid:
[assembly: ExportRenderer(typeof(MyWebView), typeof(MyProject.Droid.WebViewRenderer))]
namespace Manateq.Droid
{
public class WebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
{
public WebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
Control.SetWebViewClient(new Callback(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity));
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
MyWebView myWebView = sender as MyWebView;
if (e.PropertyName == "CurrentUrl")
{
}
}
}
public class Callback : WebViewClient
{
Activity _context;
public Callback(Activity _context)
{
this._context = _context;
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
{
//view.LoadUrl(url);
Intent i = new Intent(Intent.ActionView, Uri.Parse(url));
_context.StartActivity(i);
return true;
}
public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
DependencyService.Get<ILoadingIndicator>().Show();
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
//element.CurrentUrl = url;
DependencyService.Get<ILoadingIndicator>().Dismiss();
}
}
}
And here's I'm using the custom web view in xaml:
<customControls:MyWebView VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="webView"/>
You can got the value of CurrentUrl by MyWebView myWebView = e.NewElement as MyWebView; var currentUrl=myWebView.CurrentUrl; in OnElementChanged; Then you can transfer this value to Callback's constructor. In the end, you can set it in the OnPageFinished method by element.CurrentUrl = currenturl; like following code.
[assembly: ExportRenderer(typeof(MyWebView), typeof(WebViewRenderer))]
namespace WebviewDemo.Droid
{
public class WebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
{
public WebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
MyWebView myWebView = e.NewElement as MyWebView;
var currentUrl=myWebView.CurrentUrl;
Control.SetWebViewClient(new Callback(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity, currentUrl));
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
MyWebView myWebView = sender as MyWebView;
if (e.PropertyName == "CurrentUrl")
{
}
}
}
public class Callback : WebViewClient
{
Activity _context;
string currenturl;
public Callback(Activity _context,string currenturl)
{
this._context = _context;
this.currenturl = currenturl;
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
{
//view.LoadUrl(url);
Intent i = new Intent(Intent.ActionView, Uri.Parse(url));
_context.StartActivity(i);
return true;
}
public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
DependencyService.Get<ILoadingIndicator>().Show();
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
element.CurrentUrl = currenturl;
DependencyService.Get<ILoadingIndicator>().Dismiss();
}
}
}
Do you want to achieve the result like this GIF?
If so, you can load the current url in the OnPageFinished method.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Webkit;
using Android.Widget;
using WebviewDemo;
using WebviewDemo.Droid;
using Xamarin.Forms;
[assembly: ExportRenderer(typeof(MyWebView), typeof(WebViewRenderer))]
namespace WebviewDemo.Droid
{
public class WebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
{
public WebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
MyWebView myWebView = e.NewElement as MyWebView;
var currentUrl=myWebView.CurrentUrl;
Control.SetWebViewClient(new Callback(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity, currentUrl));
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
MyWebView myWebView = sender as MyWebView;
if (e.PropertyName == "CurrentUrl")
{
}
}
}
public class Callback : WebViewClient
{
Activity _context;
string currenturl;
public Callback(Activity _context,string currenturl)
{
this._context = _context;
this.currenturl = currenturl;
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
{
//if (!loadingFinished)
//{
// redirect = true;
//}
//loadingFinished = false;
//view.LoadUrl(currenturl);
//Console.WriteLine("Loading web view...");
return true;
}
public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
if (url.Equals("https://www.google.com/"))
{
view.LoadUrl(currenturl);
}
}
//public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
//{
// //view.LoadUrl(url);
// // Intent i = new Intent(Intent.ActionView, Uri.Parse(url));
// // _context.StartActivity(i);
// view.LoadUrl(url);
// isRedirected = true;
// return true;
//}
//public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
//{
// base.OnPageStarted(view, url, favicon);
// if (!isRedirected)
// {
// //Do something you want when starts loading
// }
// isRedirected = false;
// // DependencyService.Get<ILoadingIndicator>().Show();
//}
//public override void OnPageFinished(Android.Webkit.WebView view, string url)
//{
// base.OnPageFinished(view, url);
// if (!isRedirected)
// {
// //Do something you want when finished loading
// }
// // element.CurrentUrl = currenturl;
// // DependencyService.Get<ILoadingIndicator>().Dismiss();
//}
}
}
In the xaml. You can bind the value for CurrentUrl, Or just set it directly.
<customControls:MyWebView VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="webView"/>
Xaml background code.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
webView.Source = "https://www.google.com";
webView.CurrentUrl = "https://www.baidu.com";
}
}
The answer provided by Leon is good. But I found an easier way to do it. Here's what I've done.
In the Callback class that extends WebviewClient I've edited OnPageStarted and OnPageFinished to be as following:
public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
{
DependencyService.Get<ILoadingIndicator>().Show();
base.OnPageStarted(view, url, favicon);
var args = new WebNavigatingEventArgs(WebNavigationEvent.NewPage, new UrlWebViewSource { Url = url }, url);
_renderer.ElementController.SendNavigating(args);
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
DependencyService.Get<ILoadingIndicator>().Dismiss();
base.OnPageFinished(view, url);
var source = new UrlWebViewSource { Url = url };
var args = new WebNavigatedEventArgs(WebNavigationEvent.NewPage, source, url, WebNavigationResult.Success);
_renderer.ElementController.SendNavigated(args);
}
As for _rendered its: ElementController => Element; and it exist in the WebViewRenderer class.
When adding these line of codes, the Navigated event is being hit normally. So when that happens we can simply use the url parameter that exist in Navigated event.

Xamarin Forms Webview Custom Renderer Zoom not working

Ok, so I've been trying to get the custom renderer working but I have a problem.
The Xamarin Forums discussion is here: https://forums.xamarin.com/discussion/comment/376813#Comment_376813
The problem is tht when I zoom out the webview stays in a part of the screen and doesn't fit the whole screen.
I add the code of the project:
MyWebView.cs
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace CofarLE_Ejemplo_5
{
public class MyWebView : WebView
{
public int ZoomInLevel
{
get { return (int)GetValue(ZoomInLevelProperty); }
set { SetValue(ZoomInLevelProperty, value); }
}
public bool EnableZoomControl
{
get { return (bool)GetValue(EnableZoomControlProperty); }
set { SetValue(EnableZoomControlProperty, value); }
}
public static readonly BindableProperty ZoomInLevelProperty = BindableProperty.Create(propertyName: "ZoomInLevel", returnType: typeof(int), declaringType: typeof(MyWebView), defaultValue: 100, propertyChanged: OnZoomInLevelPropertyChanged);
public static readonly BindableProperty EnableZoomControlProperty = BindableProperty.Create(propertyName: "EnableZoomControl", returnType: typeof(bool), declaringType: typeof(MyWebView), defaultValue: false, propertyChanged: OnEnableZoomChanged);
private static void OnZoomInLevelPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control1 = (MyWebView)bindable;
control1.ZoomInLevel = (int)newValue;
}
private static void OnEnableZoomChanged(BindableObject bindable, object oldValue, object newValue)
{
var control1 = (MyWebView)bindable;
control1.EnableZoomControl = (bool)newValue;
}
}
}
MyWebViewRendereriOS.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using CofarLE_Ejemplo_5;
using CofarLE_Ejemplo_5.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(MyWebView), typeof(MyWebViewRendereriOS))]
namespace CofarLE_Ejemplo_5.iOS
{
public class MyDelegate : UIScrollViewDelegate
{
public UIView myView;
public UIView ViewForZoom;
public MyDelegate(UIView view)
{
myView = view;
ViewForZoom = view;
}
public override UIView ViewForZoomingInScrollView(UIScrollView scrollView)
{
MessagingCenter.Subscribe<object, bool>(this, "zoom", (sender, arg) => {
if (arg == true)
{
myView.ContentMode = UIViewContentMode.ScaleToFill;
ViewForZoom = myView;
}
else
{
ViewForZoom = null;
}
});
return ViewForZoom;
}
}
public class MyWebViewRendereriOS : WebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (NativeView != null && e.NewElement != null)
{
var control1 = NativeView as UIWebView;
if (e.OldElement != null)
{
e.OldElement.PropertyChanged -= OnElementPropertyChanged;
}
if (e.NewElement != null)
{
e.NewElement.PropertyChanged += OnElementPropertyChanged;
}
control1.ScalesPageToFit = true;
control1.ScrollView.Delegate = new MyDelegate(control1);
control1.ContentMode = UIViewContentMode.ScaleToFill;
control1.ScrollView.SizeToFit();
}
}
private void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var control1 = NativeView as UIWebView;
if (control1 == null)
{
return;
}
control1.ScalesPageToFit = true;
ScrollView.MaximumZoomScale = 2;
ScrollView.MinimumZoomScale = nfloat.Parse("0.5");
control1.ScrollView.MaximumZoomScale = 2;
control1.ScrollView.SizeToFit();
control1.ScrollView.MinimumZoomScale = nfloat.Parse("0.5");
}
}
}
you could try this,is it the effect you need(here is for Android)
1.create a custom view ZoomWebView:
public class ZoomWebView : View
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(ZoomWebView),
defaultValue: default(string));
public string Uri {
get { return (string)GetValue (UriProperty); }
set { SetValue (UriProperty, value); }
}
}
2. custom renderer ZoomWebViewRenderer
public class ZoomWebViewRenderer : ViewRenderer<ZoomWebView, Android.Webkit.WebView>
{
Context _context;
public ZoomWebViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<ZoomWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var webView = new Android.Webkit.WebView(_context);
webView.Settings.UseWideViewPort = true;
webView.Settings.LoadWithOverviewMode = true;
webView.Settings.SetSupportZoom(true);
webView.Settings.BuiltInZoomControls = true;
webView.Settings.DisplayZoomControls = false;
webView.SetWebViewClient(new ZoomWebViewClient());
SetNativeControl(webView);
Control.LoadUrl(Element.Uri);
}
}
class ZoomWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, IWebResourceRequest request)
{
view.LoadUrl(request.Url.ToString());
return true;
}
}
}
3.call in the page xaml:
<ContentPage.Content>
<local:ZoomWebView Uri="https://www.microsoft.com" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</ContentPage.Content>

Is there a better way to set TabbedPage bottom tab bar height than in ViewModel?

I want to change the height of the bottom TabBar in my Xamarin app. Now I am doing this via ViewModel properties:
public partial class MainPage : TabbedPage
{
public int TabBarHeight
{
get { return _tabBarHeight; }
set { _tabBarHeight = value; OnPropertyChanged(); }
}
int _tabBarHeight = 200;
And a custom renderer for iOS:
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using System.Diagnostics;
using Ja.Enums;
using System.ComponentModel;
using CoreGraphics;
[assembly: ExportRenderer(typeof(TabbedPage), typeof(Ja.iOS.TabbedPageRenderer))]
namespace Ja.iOS
{
public class TabbedPageRenderer : TabbedRenderer
{
private MainPage _page;
public TabbedPageRenderer()
{
this.ViewControllerSelected += OnTabbarControllerItemSelected;
}
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
TabBar.Frame = new CGRect(TabBar.Frame.X, TabBar.Frame.Y + (TabBar.Frame.Height - _page.TabBarHeight), TabBar.Frame.Width, _page.TabBarHeight);
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
e.OldElement.PropertyChanged -= Current_PropertyChanged;
return;
}
if (e.NewElement != null)
{
_page = (MainPage)e.NewElement;
e.NewElement.PropertyChanged += Current_PropertyChanged;
}
}
void Current_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "FrameHeight")
ViewWillLayoutSubviews();
}
}
}
Is this an optimal way to be doing this? I seem to remember having a shared class. That class would contain the code that's in the first few lines of my main as bound elements.
Could anyone comment on this and perhaps suggest a better way of doing this.
Looks like something I would use a Custom Control for (which is what I think you mean with 'shared class').
First you create the class below in your PCL. A CustomTabbedPage, which inherits from TabbedPage but has one extra property: 'TabBarHeight'
public class CustomTabbedPage : TabbedPage {
public static readonly BindableProperty TabBarHeightProperty = BindableProperty.Create("TabBarHeight", typeof(int), typeof(TabbedPage), 0);
public int TabBarHeight {
get { return (int)GetValue(TabBarHeightProperty); }
set { SetValue(TabBarHeightProperty, value); }
}
}
Now edit your renderer to a renderer of the CustomTabbedPage. Then you can easily access the TabBarHeight property by using this.Element.TabBarHeight.
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using System.Diagnostics;
using Ja.Enums;
using System.ComponentModel;
using CoreGraphics;
[assembly: ExportRenderer(typeof(CustomTabbedPage), typeof(Ja.iOS.CustomTabbedPageRenderer))]
namespace Ja.iOS
{
public class CustomTabbedPageRenderer : TabbedRenderer
{
public TabbedPageRenderer()
{
this.ViewControllerSelected += OnTabbarControllerItemSelected;
}
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
TabBar.Frame = new CGRect(TabBar.Frame.X, TabBar.Frame.Y + (TabBar.Frame.Height - this.Element.TabBarHeight), TabBar.Frame.Width, this.Element.TabBarHeight);
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null) {
e.OldElement.PropertyChanged -= Element_PropertyChanged;
}
if (e.NewElement != null) {
e.NewElement.PropertyChanged += Element_PropertyChanged;
}
}
void Current_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "FrameHeight")
ViewWillLayoutSubviews();
}
}
}
There might be some syntax errors in there but you get the point.
Shoot if you have any questions.

SwitchRender.SwitchRender()' is obsolete with Xamarin.Forms renderer

I am implementing code for SwitchRenderer in Android but it appears that there is an issue with the code as I get a warning:
'SwitchRender.SwitchRender()' is obsolete; 'This constuctor is
obsolets as of version 2.5. Please use SwitchRender(Context) instead.'
Does anyone have any ideas how I can resolve this problem:
using Japanese.Android;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Japanese;
[assembly: ExportRenderer(typeof(ExtSwitch), typeof(ExtSwitchRenderer))]
namespace Japanese.Android
{
public class ExtSwitchRenderer : SwitchRenderer
{
private ExtSwitch view;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Switch> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || e.NewElement == null)
return;
view = (ExtSwitch)Element;
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.JellyBean)
{
if (this.Control != null)
{
if (this.Control.Checked)
{
this.Control.TrackDrawable.SetColorFilter(view.SwitchOnColor.ToAndroid(), PorterDuff.Mode.SrcAtop);
}
else
{
this.Control.TrackDrawable.SetColorFilter(view.SwitchOffColor.ToAndroid(), PorterDuff.Mode.SrcAtop);
}
this.Control.CheckedChange += this.OnCheckedChange;
UpdateSwitchThumbImage(view);
}
//Control.TrackDrawable.SetColorFilter(view.SwitchBGColor.ToAndroid(), PorterDuff.Mode.Multiply);
}
}
private void UpdateSwitchThumbImage(CustomSwitch view)
{
if (!string.IsNullOrEmpty(view.SwitchThumbImage))
{
view.SwitchThumbImage = view.SwitchThumbImage.Replace(".jpg", "").Replace(".png", "");
int imgid = (int)typeof(Resource.Drawable).GetField(view.SwitchThumbImage).GetValue(null);
Control.SetThumbResource(Resource.Drawable.icon);
}
else
{
Control.ThumbDrawable.SetColorFilter(view.SwitchThumbColor.ToAndroid(), PorterDuff.Mode.Multiply);
// Control.SetTrackResource(Resource.Drawable.track);
}
}
private void OnCheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e)
{
if (this.Control.Checked)
{
this.Control.TrackDrawable.SetColorFilter(view.SwitchOnColor.ToAndroid(), PorterDuff.Mode.SrcAtop);
}
else
{
this.Control.TrackDrawable.SetColorFilter(view.SwitchOffColor.ToAndroid(), PorterDuff.Mode.SrcAtop);
}
}
protected override void Dispose(bool disposing)
{
this.Control.CheckedChange -= this.OnCheckedChange;
base.Dispose(disposing);
}
}
}
Check out the answer here. The new way to do Android custom renderers would be this:
public class ExtSwitchRenderer : SwitchRenderer
{
private ExtSwitch view;
public ExtSwitchRenderer(Context context) : base(context) { }
....
}
Then if you actually need to use the Android context in your custom renderer, you would use the one passed in through the constructor, instead of Forms.Context for example.

How to create an Entry renderer in Xamarin?

I want to create a renderer for an entry field using Xamarin. What is the best way to get it? I have no idea how to get it, any help will be appreciate.
This is my entry field and I want to create font size renderer for that
<Entry x:Name="txtTest"/>
This is sample of Entry that set padding to 0 and can set TextSize property.
namespace projectname
{
public class BareEntry : Entry
{
public BareEntry ()
{
}
public static readonly BindableProperty TextSizeProperty =
BindableProperty.Create<BareEntry,int> (X => X.TextSize, 10);
public int TextSize
{
get {return (int)base.GetValue (TextSizeProperty);}
set {base.SetValue (TextSizeProperty, value);}
}
}
}
This part is on android platform
using Xamarin.Forms;
using projectname;
using projectname.Droid;
using Xamarin.Forms.Platform.Android;
using MocMSearch.Droid;
using Android.Support.V4.Content;
[assembly: ExportRenderer (typeof (BareEntry), typeof (BareEntryRenderer))]
namespace MocMSearch.Droid
{
public class BareEntryRenderer : EntryRenderer
{
public BareEntryRenderer ()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Entry> e)
{
base.OnElementChanged(e);
Recreate ();
}
void Recreate()
{
if (this.Control == null) return;
Control.SetPadding (0,0,0,0);
Control.SetTextSize (Android.Util.ComplexUnitType.Mm,(Element as BareEntry).TextSize);
}
protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);
if (e.PropertyName == "TextSize")
{
Recreate ();
this.Invalidate ();
}
}
}
}

Resources