Xamarin.FormsWebView是在您的應用程式中顯示 Web 和 HTML 內容的檢視。 本文說明如何建立自定義轉譯器,以擴充 WebView 以允許從 JavaScript 叫用 C# 程序代碼。
每個 Xamarin.Forms 檢視都有每個平臺的隨附轉譯器,可建立原生控件的實例。 WebView當 應用程式在 Xamarin.Forms iOS 上轉譯 時,類別WkWebViewRenderer就會具現化,進而具現化原生WkWebView控件。 在 Android 平台上,WebViewRenderer 類別會具現化原生 WebView 控制項。 在通用 Windows 平台 (UWP) 上,WebViewRenderer 類別會具現化原生 WebView 控制項。 如需控件對應之轉譯器和原生控件類別 Xamarin.Forms 的詳細資訊,請參閱 轉譯器基類和原生控件。
下圖說明 View 和實作它之對應原生控制項間的關聯性:

轉譯程式可用來實作平臺自定義,方法是在每個平臺上建立的 WebView 自定義轉譯器。 執行這項作業的程序如下:
現在將討論每個專案,以實 HybridWebView 作可增強 Xamarin.FormsWebView 的轉譯器,以允許從 JavaScript 叫用 C# 程式代碼。 HybridWebView 執行個體會用以顯示 HTML 網頁,要求使用者輸入其名稱。 然後,當使用者按一下 HTML 按鈕時,JavaScript 函式會叫用 C# Action,其顯示包含使用者名稱的快顯視窗。
如需從 JavaScript 叫用 C# 的程式詳細資訊,請參閱 從 JavaScript 叫用 C# 。 如需 HTML 頁面的詳細資訊,請參閱 建立網頁。
注意
WebView可以從 C# 叫用 JavaScript 函式,並將任何結果傳回呼叫的 C# 程式代碼。 如需詳細資訊,請參閱 叫用 JavaScript。
建立 HybridWebView
自訂 HybridWebView 控制項可以藉由子類別 WebView 化來建立:
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);
}
}
HybridWebView 自訂控制項會在 .NET Standard 程式庫專案中建立,並定義下列控制項的 API:
- 指定要載入之網頁位址的
Uri屬性。 - 向控制項登錄
Action的RegisterAction方法。 已註冊的動作會從 JavaScript 叫用,此 JavaScript 包含在透過Uri屬性參考的 HTML 檔案中。 - 移除已註冊
Action之參考的CleanUp方法。 - 叫用已註冊
Action的InvokeAction方法。 這個方法將會從每個平台專案中的自定義轉譯器呼叫。
使用 HybridWebView
您可以宣告控制項的位置命名空間並使用自訂控制項上的命名空間前置詞,在 .NET Standard 程式庫專案的 XAML 中參考 HybridWebView 自訂控制項。 下列程式碼範例示範 XAML 頁面如何使用 HybridWebView 自訂控制項:
<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>
local 命名空間前置詞沒有命名限制。 不過,clr-namespace 和 assembly 值必須符合自訂控制項的詳細資料。 一旦宣告命名空間,即會使用前置詞來參考自訂控制項。
下列程式碼範例示範 C# 頁面如何使用 HybridWebView 自訂控制項:
public HybridWebViewPageCS()
{
var hybridWebView = new HybridWebView
{
Uri = "index.html"
};
// ...
Padding = new Thickness(0, 40, 0, 0);
Content = hybridWebView;
}
HybridWebView 執行個體會用於顯示每個平台的原生 Web 控制項。 Uri屬性會設定為儲存在每個平台專案中的 HTML 檔案,並由原生 Web 控件顯示。 轉譯的 HTML 會要求使用者輸入其名稱,以 JavaScript 函式叫用 C# Action 以回應 [HTML] 按鈕的按一下動作。
HybridWebViewPage 註冊要從 JavaScript 叫用的動作,如下列程式碼範例所示:
public partial class HybridWebViewPage : ContentPage
{
public HybridWebViewPage()
{
// ...
hybridWebView.RegisterAction(data => DisplayAlert("Alert", "Hello " + data, "OK"));
}
}
這個動作會呼叫 DisplayAlert 方法,顯示強制回應快顯,此快顯可呈現 HybridWebView 執行個體所顯示之 HTML 頁面中輸入的名稱。
自定義轉譯器現在可以新增至每個應用程式專案,藉由允許從 JavaScript 叫用 C# 程式代碼來增強平臺 Web 控件。
在每個平臺上建立自定義轉譯器
建立自訂轉譯器類別的流程如下:
- 在 iOS 上建立 類別的
WkWebViewRenderer子類別,以及在WebViewRendererAndroid 和 UWP 上建立類別,以轉譯自定義控件。 - 覆寫轉
OnElementChanged譯WebView和 寫入邏輯的方法,以自定義它。 建立物件時HybridWebView會呼叫這個方法。 ExportRenderer將屬性新增至自定義轉譯器類別或AssemblyInfo.cs,以指定將用來轉Xamarin.Forms譯自定義控件。 這個屬性是用來向 Xamarin.Forms註冊自定義轉譯器。
注意
對於大多數 Xamarin.Forms 元素,您可以選擇在每個平台專案中提供自定義轉譯器。 如果自訂轉譯器未註冊,則會使用控制項基底類別的預設轉譯器。 不過,轉譯 View 項目時,每個平台專案都必須要有自訂轉譯器。
下圖說明應用程式範例中每個專案的責任,以及這些專案之間的關聯性:

自定義 HybridWebView 控件是由平台轉譯器類別轉譯,其衍生自 WkWebViewRenderer iOS 上的 類別,以及 WebViewRenderer Android 和 UWP 上的 類別。 這會導致使用原生 Web 控件轉譯每個 HybridWebView 自定義控件,如下列螢幕快照所示:

WkWebViewRenderer和 WebViewRenderer 類別會公開 OnElementChanged 方法,這個方法會在建立自定義控件以轉譯對應的原生Web控件時Xamarin.Forms呼叫。 這個方法會採用 VisualElementChangedEventArgs 包含 OldElement 和 NewElement 屬性的參數。 這些屬性代表Xamarin.Forms轉譯器所附加的專案,以及Xamarin.Forms轉譯器分別附加至的專案。 在範例應用程式中,OldElement 屬性會是 null,而 NewElement 屬性會包含 HybridWebView 執行個體的參考。
在每個平台轉譯器類別中,方法的 OnElementChanged 覆寫版本是執行原生 Web 控件自定義的位置。 可以透過屬性取得所轉譯之控件的Element參考Xamarin.Forms。
每個自定義轉譯器類別都會以 ExportRenderer 向 註冊轉譯器 Xamarin.Forms的屬性裝飾。 屬性會採用兩個參數 :要轉譯之 Xamarin.Forms 自定義控件的類型名稱,以及自定義轉譯器的型別名稱。 屬性的 assembly 前置詞會指定套用至整個組件的屬性。
下列各節將討論每個原生 Web 控制項所載入的網頁結構、從 JavaScript 叫用 C# 的程式,以及每個平臺自定義轉譯器類別的實作。
建立網頁
下列程式碼範例示範將由 HybridWebView 自訂控制項顯示的網頁:
<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>
網頁可讓使用者在 input 項目中輸入他們的姓名,並提供按一下即可叫用 C# 程式碼的 button 項目。 執行這項作業的程序如下:
- 當使用者按一下
button項目時,即會呼叫invokeCSCodeJavaScript 函式,並將input項目的值傳遞至函式。 invokeCSCode函式會呼叫log函式,顯示它傳送到 C#Action的資料。 然後,它會呼叫invokeCSharpAction方法來叫用 C#Action,傳遞從input項目收到的參數。
invokeCSharpAction JavaScript 函式不是在網頁中定義,而是由每個自訂轉譯器插入至網頁。
在 iOS 上,此 HTML 檔案位於具有 BundleResource 建置動作的平台專案 [內容] 資料夾中。 在 Android 上,此 HTML 檔案則位於具有 AndroidAsset 建置動作的平台專案 [資產/內容] 資料夾中。
從 JavaScript 叫用 C#
每個平台從 JavaScript 叫用 C# 的程序都完全相同:
- 自訂轉譯器會建立原生 Web 控制項,並載入
HybridWebView.Uri屬性指定的 HTML 檔案。 - 一旦載入網頁,自訂轉譯器就會將
invokeCSharpActionJavaScript 函式插入至網頁。 - 當使用者輸入其名稱並按一下 HTML
button項目時,即會叫用invokeCSCode函式,然後接著叫用invokeCSharpAction函式。 invokeCSharpAction函式會在自訂轉譯器中叫用方法,再接著叫用HybridWebView.InvokeAction方法。HybridWebView.InvokeAction方法叫用已註冊的Action。
下列各節會討論如何在每個平台上實作此程序。
在 iOS 上建立自定義轉譯器
下列程式碼範例示範適用於 iOS 平台的自訂轉譯器:
[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);
}
}
}
HybridWebViewRenderer 類別會將 HybridWebView.Uri 屬性中所指定網頁載入到原生的 WKWebView 控制項中,而 invokeCSharpAction JavaScript 函式會插入至網頁。 一旦使用者輸入其名稱並按一下 HTML button 項目,即會使用網頁收到訊息後呼叫的 DidReceiveScriptMessage 方法來執行 invokeCSharpAction JavaScript 函式。 接著,此方法會叫用 HybridWebView.InvokeAction 方法,這樣會叫用已註冊的動作來顯示快顯。
執行此功能的程序如下:
- 轉譯器建構函式會
WkWebViewConfiguration建立 物件,並擷取其WKUserContentController物件。 對象WkUserContentController允許張貼訊息,並將使用者腳本插入網頁。 - 轉譯器建構函式會
WKUserScript建立 物件,在載入網頁之後,將JavaScript函式插入invokeCSharpAction網頁。 - 轉譯器建構函式會呼叫
WKUserContentController.AddUserScript方法,將 物件新增WKUserScript至內容控制器。 - 轉譯器建構函式會呼叫
WKUserContentController.AddScriptMessageHandler方法,將名為invokeAction的腳本訊息處理程式新增至WKUserContentController物件,這會導致 JavaScript 函window.webkit.messageHandlers.invokeAction.postMessage(data)式定義於所有使用WKUserContentController物件之實例的所有WebView框架中。 - 前提是自定義轉譯器已附加至新 Xamarin.Forms 元素:
WKWebView.LoadRequest方法會載入HybridWebView.Uri屬性所指定的 HTML 檔案。 程式碼指定該檔案會儲存在專案的Content資料夾中。 一旦顯示網頁,即將invokeCSharpActionJavaScript 函式插入至網頁。
- 當轉譯器附加至變更的專案時,就會釋放資源。
- 處置轉譯器時,會 Xamarin.Forms 清除專案。
注意
僅 iOS 8 和更新版本支援 WKWebView 類別。
此外,必須更新 Info.plist 以包含下列值:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
在Android上建立自定義轉譯器
下列程式碼範例示範適用於 Android 平台的自訂轉譯器:
[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);
}
}
}
HybridWebViewRenderer 類別會將 HybridWebView.Uri 屬性中所指定網頁載入到原生的 WebView 控制項中,並在網頁載入完成後,使用 JavascriptWebViewClient 類別的 OnPageFinished 覆寫將 invokeCSharpAction JavaScript 函式插入至網頁:
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);
}
}
一旦使用者輸入其名稱並按一下 HTML button 項目,即會執行 invokeCSharpAction JavaScript 函式。 執行此功能的程序如下:
- 前提是自定義轉譯器已附加至新 Xamarin.Forms 元素:
- 方法會將
SetWebViewClient新的JavascriptWebViewClient物件設定為的實作WebViewClient。 WebView.AddJavascriptInterface方法會將新的JSBridge執行個體插入至 WebView JavaScript 內容的主框架,將它命名為jsBridge。 以便從 JavaScript 存取JSBridge類別中的方法。WebView.LoadUrl方法會載入HybridWebView.Uri屬性所指定的 HTML 檔案。 程式碼指定該檔案會儲存在專案的Content資料夾中。- 在
JavascriptWebViewClient類別中,invokeCSharpActionJavaScript 函式會在頁面載入完成後插入至網頁。
- 方法會將
- 當轉譯器附加至變更的專案時,就會釋放資源。
- 處置轉譯器時,會 Xamarin.Forms 清除專案。
當 invokeCSharpAction JavaScript 函式執行時,它會依序叫用 JSBridge.InvokeAction 方法,如下列程式碼範例所示:
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);
}
}
}
類別必須衍生自 Java.Lang.Object,而向 JavaScript 公開的方法則必須使用 [JavascriptInterface] 和 [Export] 屬性裝飾。 因此,當 invokeCSharpAction JavaScript 函式插入至網頁且執行時,它會因為正使用 [JavascriptInterface] 和 [Export("invokeAction")] 屬性裝飾而呼叫 JSBridge.InvokeAction 方法。 接著, InvokeAction 方法會叫 HybridWebView.InvokeAction 用 方法,這會叫用已註冊的動作以顯示快顯。
重要
使用 屬性的 [Export] Android 項目必須包含 的 Mono.Android.Export參考,否則會產生編譯程序錯誤。
請注意,JSBridge 類別會將 WeakReference 維持為 HybridWebViewRenderer 類別。 這是為了避免建立兩個類別之間的循環參考。 如需詳細資訊,請參閱 弱式參考。
在UWP上建立自定義轉譯器
下列程式碼範例示範適用於 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);
}
}
}
HybridWebViewRenderer 類別會將 HybridWebView.Uri 屬性中所指定網頁載入到原生的 WebView 控制項中,並在網頁載入後,使用 WebView.InvokeScriptAsync 方法將 invokeCSharpAction JavaScript 函式插入至網頁。 一旦使用者輸入其名稱並按一下 HTML button 項目,即會使用網頁收到通知後呼叫的 OnWebViewScriptNotify 方法來執行 invokeCSharpAction JavaScript 函式。 接著,此方法會叫用 HybridWebView.InvokeAction 方法,這樣會叫用已註冊的動作來顯示快顯。
執行此功能的程序如下:
- 前提是自定義轉譯器已附加至新 Xamarin.Forms 元素:
- 則會註冊
NavigationCompleted和ScriptNotify事件的事件處理常式。 當原生WebView控制項已完成載入目前的內容或導覽失敗時,會引發NavigationCompleted事件。 當原生WebView控制項使用 JavaScript 將字串傳遞至應用程式時,會引發ScriptNotify事件。 網頁在傳遞string參數時呼叫window.external.notify,藉以引發ScriptNotify事件。 WebView.Source屬性設為HybridWebView.Uri屬性所指定之 HTML 檔案的 URI。 程式碼假設該檔案會儲存在專案的Content資料夾中。 一旦顯示網頁,就會引發NavigationCompleted事件並叫用OnWebViewNavigationCompleted方法。 然後使用WebView.InvokeScriptAsync方法將invokeCSharpActionJavaScript 函式插入至網頁,假設導覽已順利完成。
- 則會註冊
- 當轉譯器附加至變更的專案時,事件會取消訂閱。
- 處置轉譯器時,會 Xamarin.Forms 清除專案。