Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Это Xamarin.FormsWebView представление, отображающее веб-содержимое и HTML в приложении. В этой статье рассказывается, как создать пользовательский отрисовщик, расширяющий возможности WebView и позволяющий вызывать код C# из JavaScript.
Каждое представление Xamarin.Forms имеет сопутствующий отрисовщик для каждой платформы, который создает экземпляр собственного элемента управления. Когда WebView отображается в приложении Xamarin.Forms, на платформе iOS создается класс WkWebViewRenderer, который, в свою очередь, создает собственный элемент управления WkWebView. На платформе Android класс WebViewRenderer создает собственный элемент управления WebView. На универсальной платформе Windows (UWP) класс WebViewRenderer создает экземпляр собственного элемента управления WebView. Дополнительные сведения об отрисовщике и классах собственных элементов управления, с которыми сопоставляются элементы управления Xamarin.Forms, см. в статье Базовые классы и собственные элементы управления отрисовщика.
На следующей схеме показана связь между классом View и соответствующими собственными элементами управления, которые его реализуют:

Процесс отрисовки можно использовать для реализации настроек для конкретных платформ путем создания пользовательского отрисовщика для WebView на каждой платформе. Этот процесс выглядит следующим образом:
- Создание пользовательского элемента управления
HybridWebView. - Использование
HybridWebViewиз Xamarin.Forms. - Создание настраиваемого отрисовщика для
HybridWebViewна каждой платформе.
Теперь каждый элемент будет обсуждаться в свою очередь для реализации HybridWebView отрисовщика, который улучшает Xamarin.FormsWebView возможность вызова кода C# из JavaScript. Экземпляр HybridWebView будет использоваться для отображения HTML-страницы, которая предлагает пользователю ввести свое имя. Затем, когда пользователь нажимает кнопку HTML, функция JavaScript вызывает C# Action, который отображает всплывающее окно, содержащее имя пользователя.
Дополнительные сведения о процессе вызова C# из JavaScript см. в разделе Вызов C# из JavaScript. Дополнительные сведения о странице HTML см. в разделе Создание веб-страницы.
Примечание.
WebView может вызывать функцию JavaScript из C# и возвращать любой результат в вызывающий код 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, которое указывает адрес веб-страницы для загрузки. - Метод
RegisterAction, который регистрируетActionс элементом управления. Зарегистрированное действие будет вызываться из JavaScript, содержащегося в файле HTML, на который можно сослаться через свойствоUri. - Метод
CleanUp, который удаляет ссылку на зарегистрированный объектAction. - Метод
InvokeAction, который вызывает зарегистрированный объектAction. Этот метод будет вызываться из пользовательского отрисовщика в проекте для каждой платформы.
Использование HybridWebView
На пользовательский элемент управления HybridWebView можно ссылаться в XAML в проекте библиотеки .NET Standard, объявив пространство имен для его расположения и используя префикс пространства имен в пользовательском элементе управления. В следующем примере кода показано, как пользовательский элемент управления HybridWebView может использоваться страницей XAML.
<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 должны соответствовать данным пользовательского элемента управления. После объявления пространства имен префикс используется для ссылки на пользовательский элемент управления.
В следующем примере кода показано, как пользовательский элемент управления HybridWebView может использоваться страницей C#:
public HybridWebViewPageCS()
{
var hybridWebView = new HybridWebView
{
Uri = "index.html"
};
// ...
Padding = new Thickness(0, 40, 0, 0);
Content = hybridWebView;
}
Экземпляр HybridWebView будет использоваться для отображения собственного веб-элемента управления на каждой платформе. Для его свойства Uri задан HTML-файл, который хранится в проекте для каждой платформы и будет отображаться собственным веб-элементом управления. Отрисованный HTML просит пользователя ввести свое имя с помощью JavaScript, вызывая C# Action в ответ на нажатие кнопки HTML.
HybridWebViewPage регистрирует действие, которое необходимо вызывать из JavaScript, как показано в следующем примере кода:
public partial class HybridWebViewPage : ContentPage
{
public HybridWebViewPage()
{
// ...
hybridWebView.RegisterAction(data => DisplayAlert("Alert", "Hello " + data, "OK"));
}
}
Это действие вызывает метод DisplayAlert для отображения модального всплывающего окна, которое представляет имя, введенное на странице HTML, отображаемой экземпляром HybridWebView.
Настраиваемый отрисовщик теперь можно добавить в каждый проект приложения для улучшения зависящих от платформы веб-элементов управления благодаря возможности вызова кода C# из JavaScript.
Создание пользовательского отрисовщика на каждой платформе
Процесс создания класса пользовательского отрисовщика выглядит следующим образом:
- Создайте подкласс класса
WkWebViewRendererв iOS и классаWebViewRendererв Android и UWP, который отрисовывает пользовательский элемент управления. - Переопределите метод
OnElementChanged, который отрисовываетWebView, и напишите логику для его настройки. Этот метод вызывается при создании объектаHybridWebView. - Добавьте атрибут
ExportRendererв класс пользовательского отрисовщика или AssemblyInfo.cs, чтобы указать, что он будет использоваться для отрисовки пользовательского элемента управления Xamarin.Forms. Этот атрибут используется для регистрации пользовательского отрисовщика в Xamarin.Forms.
Примечание.
Для большинства элементов Xamarin.Forms предоставлять пользовательский отрисовщик в проекте для каждой платформы необязательно. Если пользовательский отрисовщик не зарегистрирован, будет использоваться отрисовщик по умолчанию для базового класса элемента управления. Однако настраиваемые отрисовщики необходимы в каждом зависящем от платформы проекте при отрисовке элемента View.
На следующей схеме показаны обязанности каждого проекта в примере приложения, а также связи между ними:

Пользовательский элемент управления HybridWebView отрисовывается с помощью зависящих от платформы классов отрисовщика, которые являются производными от класса WkWebViewRenderer в iOS и класса WebViewRenderer в Android и UWP. Это приводит к тому, что каждый пользовательский элемент управления HybridWebView отрисовывается с помощью собственных веб-элементов управления, как показано на следующих снимках экрана:

Классы WkWebViewRenderer и WebViewRenderer предоставляют метод OnElementChanged, который вызывается при создании пользовательского элемента управления Xamarin.Forms для отрисовки соответствующего собственного веб-элемента управления. Этот метод принимает параметр VisualElementChangedEventArgs, содержащий свойства OldElement и NewElement. Эти свойства представляют элемент Xamarin.Forms, к которому был присоединен отрисовщик, и элемент Xamarin.Forms, к которому присоединен отрисовщик, соответственно. В примере приложения свойство OldElement будет иметь значение null, а свойство NewElement будет содержать ссылку на экземпляр HybridWebView.
Настройка собственного веб-элемента управления выполняется в переопределенной версии метода OnElementChanged в классе отрисовщика для каждой платформы. Ссылку на отрисовываемый элемент управления Xamarin.Forms можно получить с помощью свойства Element.
Каждый класс пользовательского отрисовщика дополняется атрибутом ExportRenderer, который регистрирует отрисовщик в Xamarin.Forms. Атрибут принимает два параметра — имя типа отрисовываемого пользовательского элемента управления Xamarin.Forms и имя типа пользовательского отрисовщика. Префикс assembly в атрибуте указывает, что атрибут применяется ко всей сборке.
В следующих разделах рассматриваются структура веб-страницы, загружаемой каждым собственным веб-элементом управления, процесс вызова C# из JavaScript и реализация этого в классах пользовательского отрисовщика для каждой платформы.
Создание веб-страницы
В следующем примере кода показана веб-страница, которая будет отображаться пользовательским элементом управления 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 и предоставляет элемент button, который будет вызывать код C# при нажатии. Этот процесс выглядит следующим образом:
- Когда пользователь нажимает на элемент
button, вызывается функция JavaScriptinvokeCSCode, и значение элементаinputпередается в функцию. - Функция
invokeCSCodeвызывает функциюlogдля отображения данных, которые она отправляет в C#Action. Затем она вызывает методinvokeCSharpAction, чтобы вызывать C#Action, передавая параметр, полученный от элементаinput.
Функция JavaScript invokeCSharpAction не определена на веб-странице и внедряется в нее каждым настраиваемым отрисовщиком.
На iOS этот HTML-файл находится в папке Content проекта платформы с действием сборки BundleResource. На Android этот HTML-файл находится в папке Assets/Content проекта платформы с действием сборки AndroidAsset.
Вызов C# из JavaScript
Процесс вызова C# из JavaScript идентичен на каждой платформе:
- Настраиваемый отрисовщик создает собственный веб-элемент управления и загружает файл HTML, заданный свойством
HybridWebView.Uri. - После загрузки веб-страницы настраиваемый отрисовщик внедряет функцию JavaScript
invokeCSharpActionв веб-страницу. - Когда пользователь вводит свое имя и нажимает на элемент 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, а функция JavaScript invokeCSharpAction внедряется в веб-страницы. Когда пользователь вводит свое имя и нажимает элемент HTML button, выполняется функция JavaScript invokeCSharpAction, и метод DidReceiveScriptMessage вызывается после получения сообщения от веб-страницы. В свою очередь, этот метод вызывает метод HybridWebView.InvokeAction, который будет вызывать зарегистрированное действие для отображения всплывающего окна.
Это достигается следующим образом:
- Конструктор отрисовщика создает объект
WkWebViewConfigurationи получает его объектWKUserContentController. ОбъектWkUserContentControllerпозволяет размещать сообщения и внедрять скрипты пользователя в веб-страницу. - Конструктор отрисовщика создает объект
WKUserScript, который внедряет функцию JavaScriptinvokeCSharpActionв веб-страницу после загрузки веб-страницы. - Конструктор отрисовщика вызывает метод
WKUserContentController.AddUserScriptдля добавления объектаWKUserScriptв контроллер содержимого. - Конструктор отрисовщика вызывает метод
WKUserContentController.AddScriptMessageHandlerдля добавления обработчика сообщений скрипта с именемinvokeActionв объектWKUserContentController, который приводит к определению функции JavaScriptwindow.webkit.messageHandlers.invokeAction.postMessage(data)во всех фреймах в экземплярахWebView, которые используют объектWKUserContentController. - При условии, что пользовательский отрисовщик присоединен к новому Xamarin.Forms элементу:
- Метод
WKWebView.LoadRequestзагружает HTML-файл, указанный свойствомHybridWebView.Uri. Код указывает, что файл сохранен в папкеContentпроекта. Когда веб-страница отображается, функция JavaScriptinvokeCSharpActionвнедряется в веб-страницу.
- Метод
- Ресурсы освобождаются, когда элемент, к которому присоединен отрисовщик, меняется.
- При удалении отрисовщика элемент Xamarin.Forms очищается.
Примечание.
Класс WKWebView поддерживается только в iOS 8 и более поздних версий.
Кроме того, необходимо изменить файл 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, а функция JavaScript invokeCSharpAction внедряется в веб-страницы после окончания загрузки веб-страницы с переопределением OnPageFinished в классе JavascriptWebViewClient:
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, выполняется функция JavaScript invokeCSharpAction. Это достигается следующим образом:
- При условии, что пользовательский отрисовщик присоединен к новому Xamarin.Forms элементу:
- Метод
SetWebViewClientзадает новый объектJavascriptWebViewClientв качестве реализацииWebViewClient. - Метод
WebView.AddJavascriptInterfaceвставляет новый экземплярJSBridgeв главный фрейм контекста JavaScript веб-преставления, называя егоjsBridge. Это открывает доступ к методам в классеJSBridgeиз JavaScript. - Метод
WebView.LoadUrlзагружает HTML-файл, указанный свойствомHybridWebView.Uri. Код указывает, что файл сохранен в папкеContentпроекта. - В классе
JavascriptWebViewClientфункция JavaScriptinvokeCSharpActionвнедряется в веб-страницу после завершения загрузки страницы.
- Метод
- Ресурсы освобождаются, когда элемент, к которому присоединен отрисовщик, меняется.
- При удалении отрисовщика элемент Xamarin.Forms очищается.
Когда выполняется функция JavaScript invokeCSharpAction, она, в свою очередь, вызывает метод 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]. Таким образом, когда функция JavaScript invokeCSharpAction внедряется в веб-страницу и выполняется, она вызывает метод JSBridge.InvokeAction, поскольку имеет атрибуты [JavascriptInterface] и [Export("invokeAction")]. В свою очередь, метод InvokeAction вызывает метод HybridWebView.InvokeAction, который будет вызывать зарегистрированное действие для отображения всплывающего окна.
Внимание
Проекты Android, в которых используется атрибут [Export], должны содержать ссылку на 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, а функция JavaScript invokeCSharpAction внедряется в веб-страницы после загрузки веб-страницы с методом WebView.InvokeScriptAsync. Когда пользователь вводит свое имя и нажимает элемент HTML button, выполняется функция JavaScript invokeCSharpAction, и метод OnWebViewScriptNotify вызывается после получения уведомления от веб-страницы. В свою очередь, этот метод вызывает метод HybridWebView.InvokeAction, который будет вызывать зарегистрированное действие для отображения всплывающего окна.
Это достигается следующим образом:
- При условии, что пользовательский отрисовщик присоединен к новому Xamarin.Forms элементу:
- Обработчики событий для событий
NavigationCompletedиScriptNotifyрегистрируются. СобытиеNavigationCompletedвозникает, когда собственный элемент управленияWebViewзавершил загрузку текущего содержимого или произошел сбой навигации. СобытиеScriptNotifyвозникает, когда содержимое в собственном элементе управленияWebViewиспользует JavaScript для передачи строки в приложение. Веб-страница активирует событиеScriptNotifyпутем вызоваwindow.external.notifyс передачей параметраstring. - Свойство
WebView.Sourceполучает значение URI HTML-файла, который задается свойствомHybridWebView.Uri. Код предполагает, что файл сохранен в папкеContentпроекта. Когда веб-страница отобразится, событиеNavigationCompletedсработает и методOnWebViewNavigationCompletedбудет вызван. Функция JavaScriptinvokeCSharpActionбудет внедрена в веб-страницу с помощью методаWebView.InvokeScriptAsync, если навигация успешно завершена.
- Обработчики событий для событий
- Подписка на это событие отменяется, если элемент Xamarin.Forms, к которому присоединен отрисовщик, изменяется.
- При удалении отрисовщика элемент Xamarin.Forms очищается.