Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
A Xamarin.Forms WebView is a view that displays web and HTML content in your app. This article explains how to create a custom renderer that extends the WebView to allow C# code to be invoked from JavaScript.
Every Xamarin.Forms view has an accompanying renderer for each platform that creates an instance of a native control. When a WebView is rendered by a Xamarin.Forms application on iOS, the WkWebViewRenderer class is instantiated, which in turn instantiates a native WkWebView control. On the Android platform, the WebViewRenderer class instantiates a native WebView control. On the Universal Windows Platform (UWP), the WebViewRenderer class instantiates a native WebView control. For more information about the renderer and native control classes that Xamarin.Forms controls map to, see Renderer Base Classes and Native Controls.
The following diagram illustrates the relationship between the View and the corresponding native controls that implement it:

The rendering process can be used to implement platform customizations by creating a custom renderer for a WebView on each platform. The process for doing this is as follows:
- Create the
HybridWebViewcustom control. - Consume the
HybridWebViewfrom Xamarin.Forms. - Create the custom renderer for the
HybridWebViewon each platform.
Each item will now be discussed in turn to implement a HybridWebView renderer that enhances the Xamarin.Forms WebView to allow C# code to be invoked from JavaScript. The HybridWebView instance will be used to display an HTML page that asks the user to enter their name. Then, when the user clicks an HTML button, a JavaScript function will invoke a C# Action that displays a pop-up containing the users name.
For more information about the process for invoking C# from JavaScript, see Invoke C# from JavaScript. For more information about the HTML page, see Create the Web Page.
Note
A WebView can invoke a JavaScript function from C#, and return any result to the calling C# code. For more information, see Invoking JavaScript.
Create the HybridWebView
The HybridWebView custom control can be created by subclassing the WebView class:
public class HybridWebView : WebView
{
Action<string> action;
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public void RegisterAction(Action<string> callback)
{
action = callback;
}
public void Cleanup()
{
action = null;
}
public void InvokeAction(string data)
{
if (action == null || data == null)
{
return;
}
action.Invoke(data);
}
}
The HybridWebView custom control is created in the .NET Standard library project and defines the following API for the control:
- A
Uriproperty that specifies the address of the web page to be loaded. - A
RegisterActionmethod that registers anActionwith the control. The registered action will be invoked from JavaScript contained in the HTML file referenced through theUriproperty. - A
CleanUpmethod that removes the reference to the registeredAction. - An
InvokeActionmethod that invokes the registeredAction. This method will be called from a custom renderer in each platform project.
Consume the HybridWebView
The HybridWebView custom control can be referenced in XAML in the .NET Standard library project by declaring a namespace for its location and using the namespace prefix on the custom control. The following code example shows how the HybridWebView custom control can be consumed by a XAML page:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
x:Class="CustomRenderer.HybridWebViewPage"
Padding="0,40,0,0">
<local:HybridWebView x:Name="hybridWebView"
Uri="index.html" />
</ContentPage>
The local namespace prefix can be named anything. However, the clr-namespace and assembly values must match the details of the custom control. Once the namespace is declared, the prefix is used to reference the custom control.
The following code example shows how the HybridWebView custom control can be consumed by a C# page:
public HybridWebViewPageCS()
{
var hybridWebView = new HybridWebView
{
Uri = "index.html"
};
// ...
Padding = new Thickness(0, 40, 0, 0);
Content = hybridWebView;
}
The HybridWebView instance will be used to display a native web control on each platform. It's Uri property is set to an HTML file that is stored in each platform project, and which will be displayed by the native web control. The rendered HTML asks the user to enter their name, with a JavaScript function invoking a C# Action in response to an HTML button click.
The HybridWebViewPage registers the action to be invoked from JavaScript, as shown in the following code example:
public partial class HybridWebViewPage : ContentPage
{
public HybridWebViewPage()
{
// ...
hybridWebView.RegisterAction(data => DisplayAlert("Alert", "Hello " + data, "OK"));
}
}
This action calls the DisplayAlert method to display a modal pop-up that presents the name entered in the HTML page displayed by the HybridWebView instance.
A custom renderer can now be added to each application project to enhance the platform web controls by allowing C# code to be invoked from JavaScript.
Create the custom renderer on each platform
The process for creating the custom renderer class is as follows:
- Create a subclass of the
WkWebViewRendererclass on iOS, and theWebViewRendererclass on Android and UWP, that renders the custom control. - Override the
OnElementChangedmethod that renders theWebViewand write logic to customize it. This method is called when aHybridWebViewobject is created. - Add an
ExportRendererattribute to the custom renderer class or AssemblyInfo.cs, to specify that it will be used to render the Xamarin.Forms custom control. This attribute is used to register the custom renderer with Xamarin.Forms.
Note
For most Xamarin.Forms elements, it is optional to provide a custom renderer in each platform project. If a custom renderer isn't registered, then the default renderer for the control's base class will be used. However, custom renderers are required in each platform project when rendering a View element.
The following diagram illustrates the responsibilities of each project in the sample application, along with the relationships between them:

The HybridWebView custom control is rendered by platform renderer classes, which derive from the WkWebViewRenderer class on iOS, and from the WebViewRenderer class on Android and UWP. This results in each HybridWebView custom control being rendered with native web controls, as shown in the following screenshots:

The WkWebViewRenderer and WebViewRenderer classes expose the OnElementChanged method, which is called when the Xamarin.Forms custom control is created to render the corresponding native web control. This method takes a VisualElementChangedEventArgs parameter that contains OldElement and NewElement properties. These properties represent the Xamarin.Forms element that the renderer was attached to, and the Xamarin.Forms element that the renderer is attached to, respectively. In the sample application the OldElement property will be null and the NewElement property will contain a reference to the HybridWebView instance.
An overridden version of the OnElementChanged method, in each platform renderer class, is the place to perform the native web control customization. A reference to the Xamarin.Forms control that's being rendered can be obtained through the Element property.
Each custom renderer class is decorated with an ExportRenderer attribute that registers the renderer with Xamarin.Forms. The attribute takes two parameters – the type name of the Xamarin.Forms custom control being rendered, and the type name of the custom renderer. The assembly prefix to the attribute specifies that the attribute applies to the entire assembly.
The following sections discuss the structure of the web page loaded by each native web control, the process for invoking C# from JavaScript, and the implementation of this in each platform custom renderer class.
Create the web page
The following code example shows the web page that will be displayed by the HybridWebView custom control:
<html>
<body>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<h1>HybridWebView Test</h1>
<br />
Enter name: <input type="text" id="name">
<br />
<br />
<button type="button" onclick="javascript: invokeCSCode($('#name').val());">Invoke C# Code</button>
<br />
<p id="result">Result:</p>
<script type="text/javascript">function log(str) {
$('#result').text($('#result').text() + " " + str);
}
function invokeCSCode(data) {
try {
log("Sending Data:" + data);
invokeCSharpAction(data);
}
catch (err) {
log(err);
}
}</script>
</body>
</html>
The web page allows a user to enter their name in an input element, and provides a button element that will invoke C# code when clicked. The process for achieving this is as follows:
- When the user clicks on the
buttonelement, theinvokeCSCodeJavaScript function is called, with the value of theinputelement being passed to the function. - The
invokeCSCodefunction calls thelogfunction to display the data it is sending to the C#Action. It then calls theinvokeCSharpActionmethod to invoke the C#Action, passing the parameter received from theinputelement.
The invokeCSharpAction JavaScript function is not defined in the web page, and will be injected into it by each custom renderer.
On iOS, this HTML file resides in the Content folder of the platform project, with a build action of BundleResource. On Android, this HTML file resides in the Assets/Content folder of the platform project, with a build action of AndroidAsset.
Invoke C# from JavaScript
The process for invoking C# from JavaScript is identical on each platform:
- The custom renderer creates a native web control and loads the HTML file specified by the
HybridWebView.Uriproperty. - Once the web page is loaded, the custom renderer injects the
invokeCSharpActionJavaScript function into the web page. - When the user enters their name and clicks on the HTML
buttonelement, theinvokeCSCodefunction is invoked, which in turn invokes theinvokeCSharpActionfunction. - The
invokeCSharpActionfunction invokes a method in the custom renderer, which in turn invokes theHybridWebView.InvokeActionmethod. - The
HybridWebView.InvokeActionmethod invokes the registeredAction.
The following sections will discuss how this process is implemented on each platform.
Create the custom renderer on iOS
The following code example shows the custom renderer for the iOS platform:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.iOS
{
public class HybridWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler
{
const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
WKUserContentController userController;
public HybridWebViewRenderer() : this(new WKWebViewConfiguration())
{
}
public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
userController = config.UserContentController;
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
HybridWebView hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
string filename = Path.Combine(NSBundle.MainBundle.BundlePath, $"Content/{((HybridWebView)Element).Uri}");
LoadRequest(new NSUrlRequest(new NSUrl(filename, false)));
}
}
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
((HybridWebView)Element).InvokeAction(message.Body.ToString());
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((HybridWebView)Element).Cleanup();
}
base.Dispose(disposing);
}
}
}
The HybridWebViewRenderer class loads the web page specified in the HybridWebView.Uri property into a native WKWebView control, and the invokeCSharpAction JavaScript function is injected into the web page. Once the user enters their name and clicks the HTML button element, the invokeCSharpAction JavaScript function is executed, with the DidReceiveScriptMessage method being called after a message is received from the web page. In turn, this method invokes the HybridWebView.InvokeAction method, which will invoke the registered action to display the pop-up.
This functionality is achieved as follows:
- The renderer constructor creates a
WkWebViewConfigurationobject, and retrieves itsWKUserContentControllerobject. TheWkUserContentControllerobject allows posting messages and injecting user scripts into a web page. - The renderer constructor creates a
WKUserScriptobject, which injects theinvokeCSharpActionJavaScript function into the web page after the web page is loaded. - The renderer constructor calls the
WKUserContentController.AddUserScriptmethod to add theWKUserScriptobject to the content controller. - The renderer constructor calls the
WKUserContentController.AddScriptMessageHandlermethod to add a script message handler namedinvokeActionto theWKUserContentControllerobject, which will cause the JavaScript functionwindow.webkit.messageHandlers.invokeAction.postMessage(data)to be defined in all frames in allWebViewinstances that use theWKUserContentControllerobject. - Provided that the custom renderer is attached to a new Xamarin.Forms element:
- The
WKWebView.LoadRequestmethod loads the HTML file that's specified by theHybridWebView.Uriproperty. The code specifies that the file is stored in theContentfolder of the project. Once the web page is displayed, theinvokeCSharpActionJavaScript function will be injected into the web page.
- The
- Resources are released when the element the renderer is attached to changes.
- The Xamarin.Forms element is cleaned up when the renderer is disposed of.
Note
The WKWebView class is only supported in iOS 8 and later.
In addition, Info.plist must be updated to include the following values:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Create the custom renderer on android
The following code example shows the custom renderer for the Android platform:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.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 (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
((HybridWebView)Element).Cleanup();
}
if (e.NewElement != null)
{
Control.SetWebViewClient(new JavascriptWebViewClient(this, $"javascript: {JavascriptFunction}"));
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl($"file:///android_asset/Content/{((HybridWebView)Element).Uri}");
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((HybridWebView)Element).Cleanup();
}
base.Dispose(disposing);
}
}
}
The HybridWebViewRenderer class loads the web page specified in the HybridWebView.Uri property into a native WebView control, and the invokeCSharpAction JavaScript function is injected into the web page, after the web page has finished loading, with the OnPageFinished override in the JavascriptWebViewClient class:
public class JavascriptWebViewClient : FormsWebViewClient
{
string _javascript;
public JavascriptWebViewClient(HybridWebViewRenderer renderer, string javascript) : base(renderer)
{
_javascript = javascript;
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}
Once the user enters their name and clicks the HTML button element, the invokeCSharpAction JavaScript function is executed. This functionality is achieved as follows:
- Provided that the custom renderer is attached to a new Xamarin.Forms element:
- The
SetWebViewClientmethod sets a newJavascriptWebViewClientobject as the implementation ofWebViewClient. - The
WebView.AddJavascriptInterfacemethod injects a newJSBridgeinstance into the main frame of the WebView's JavaScript context, naming itjsBridge. This allows methods in theJSBridgeclass to be accessed from JavaScript. - The
WebView.LoadUrlmethod loads the HTML file that's specified by theHybridWebView.Uriproperty. The code specifies that the file is stored in theContentfolder of the project. - In the
JavascriptWebViewClientclass, theinvokeCSharpActionJavaScript function is injected into the web page once the page has finished loading.
- The
- Resources are released when the element the renderer is attached to changes.
- The Xamarin.Forms element is cleaned up when the renderer is disposed of.
When the invokeCSharpAction JavaScript function is executed, it in turn invokes the JSBridge.InvokeAction method, which is shown in the following code example:
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);
}
}
}
The class must derive from Java.Lang.Object, and methods that are exposed to JavaScript must be decorated with the [JavascriptInterface] and [Export] attributes. Therefore, when the invokeCSharpAction JavaScript function is injected into the web page and is executed, it will call the JSBridge.InvokeAction method due to being decorated with the [JavascriptInterface] and [Export("invokeAction")] attributes. In turn, the InvokeAction method invokes the HybridWebView.InvokeAction method, which will invoke the registered action to display the pop-up.
Important
Android projects that use the [Export] attribute must include a reference to Mono.Android.Export, or a compiler error will result.
Note that the JSBridge class maintains a WeakReference to the HybridWebViewRenderer class. This is to avoid creating a circular reference between the two classes. For more information see Weak References.
Create the custom renderer on UWP
The following code example shows the custom renderer for UWP:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.UWP
{
public class HybridWebViewRenderer : WebViewRenderer
{
const string JavaScriptFunction = "function invokeCSharpAction(data){window.external.notify(data);}";
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.NavigationCompleted -= OnWebViewNavigationCompleted;
Control.ScriptNotify -= OnWebViewScriptNotify;
}
if (e.NewElement != null)
{
Control.NavigationCompleted += OnWebViewNavigationCompleted;
Control.ScriptNotify += OnWebViewScriptNotify;
Control.Source = new Uri($"ms-appx-web:///Content//{((HybridWebView)Element).Uri}");
}
}
async void OnWebViewNavigationCompleted(Windows.UI.Xaml.Controls.WebView sender, WebViewNavigationCompletedEventArgs args)
{
if (args.IsSuccess)
{
// Inject JS script
await Control.InvokeScriptAsync("eval", new[] { JavaScriptFunction });
}
}
void OnWebViewScriptNotify(object sender, NotifyEventArgs e)
{
((HybridWebView)Element).InvokeAction(e.Value);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((HybridWebView)Element).Cleanup();
}
base.Dispose(disposing);
}
}
}
The HybridWebViewRenderer class loads the web page specified in the HybridWebView.Uri property into a native WebView control, and the invokeCSharpAction JavaScript function is injected into the web page, after the web page has loaded, with the WebView.InvokeScriptAsync method. Once the user enters their name and clicks the HTML button element, the invokeCSharpAction JavaScript function is executed, with the OnWebViewScriptNotify method being called after a notification is received from the web page. In turn, this method invokes the HybridWebView.InvokeAction method, which will invoke the registered action to display the pop-up.
This functionality is achieved as follows:
- Provided that the custom renderer is attached to a new Xamarin.Forms element:
- Event handlers for the
NavigationCompletedandScriptNotifyevents are registered. TheNavigationCompletedevent fires when either the nativeWebViewcontrol has finished loading the current content or if navigation has failed. TheScriptNotifyevent fires when the content in the nativeWebViewcontrol uses JavaScript to pass a string to the application. The web page fires theScriptNotifyevent by callingwindow.external.notifywhile passing astringparameter. - The
WebView.Sourceproperty is set to the URI of the HTML file that's specified by theHybridWebView.Uriproperty. The code assumes that the file is stored in theContentfolder of the project. Once the web page is displayed, theNavigationCompletedevent will fire and theOnWebViewNavigationCompletedmethod will be invoked. TheinvokeCSharpActionJavaScript function will then be injected into the web page with theWebView.InvokeScriptAsyncmethod, provided that the navigation completed successfully.
- Event handlers for the
- Event are unsubscribed from when the element the renderer is attached to changes.
- The Xamarin.Forms element is cleaned up when the renderer is disposed of.