Xamarin.Forms (UWP) - Как я могу получить DOM WebView в виде строки HTML?

В проекте Xamarin.Forms (UWP) у меня есть WebView контролировать чей Source создается со строкой HTML, например так:

var webview = new Xamarin.Forms.WebView
{
    Source = new HtmlWebViewSource
    {
        Html = "<html>....</html>"
    }
};

HTML содержит JavaScript, который динамически генерирует HTML внутри <body>, Это прекрасно отображает на экране. Это означает, что WebView понимает DOM, который создается с помощью JavaScript. Отлично.

Но теперь мне нужно проанализировать некоторые из сгенерированных HTML, но все, что мне может показаться, - это доступ к исходной строке HTML, которую я передал в качестве источника, а не к окончательному сгенерированному DOM.

Есть ли способ преобразовать DOM, сгенерированный JavaScript и понятый WebView, в строку, чтобы я мог проанализировать (используя библиотеку, такую ​​как HTML Agility Pack или AngleSharp) и извлечь некоторые сегменты HTML? Это может быть в Xamarin.Forms или UWP (платформа, на которую я нацеливаюсь).

ПРИМЕЧАНИЕ. В полном раскрытии (если это помогает, и чтобы избежать обвинений в том, что это проблема XY), я в конечном итоге пытаюсь решить проблему печати WebView с несколькими страницами на UWP - исследования по этому вопросу встречаются очень редко Информация. У меня есть решение, которое работает для HTML, которое не генерируется динамически с помощью JavaScript - в основном я извлекаю части HTML, представляющие печатные страницы, и добавляю их как отдельные страницы для печати и предварительного просмотра. Но, как упоминалось ранее, я не могу разобрать динамически генерируемый контент.

1 ответ

Моей первой мыслью было использовать Eval Метод встроен в Xamarin.Forms, но потом я узнал, что этот метод ничего не возвращает, поэтому он подходит только для связи между приложениями и веб-представлениями.

Пока что самый простой способ реализовать это - использовать пользовательскую версию WebView управления:

public class ExtendedWebView : WebView
{
    public delegate Task<string> GetHtmlRequestedHandler();

    public event GetHtmlRequestedHandler GetHtmlRequested;


    public async Task<string> GetHtmlAsync()
    {
        var handler = GetHtmlRequested;
        if (handler != null)
        {
            return await handler.Invoke();
        }
        return null;
    }
}

Теперь в проекте платформы UWP создайте пользовательский рендер:

[assembly: ExportRenderer(typeof(ExtendedWebView), typeof(ExtendedWebViewRenderer))]
namespace App.UWP
{
    public class ExtendedWebViewRenderer : WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement != null)
            {
                var ew = (e.OldElement as ExtendedWebView);
                ew.GetHtmlRequested -= Ew_GetHtmlRequested;
            }

            if (e.NewElement != null)
            {
                var ew = (e.NewElement as ExtendedWebView);
                ew.GetHtmlRequested += Ew_GetHtmlRequested;
            }
        }

        private async Task<string> Ew_GetHtmlRequested()
        {
            return await Control.InvokeScriptAsync("eval", new string[] { "document.documentElement.outerHTML;" });
        }
    }
}

Хитрость в том, что мы вызываем JavaScript eval функция, которая будет возвращать сам HTML из веб-представления.

Вам просто нужно заменить WebView в XAML с нашими ExtendedWebView и назовите его GetHtmlAsync метод всякий раз, когда это необходимо.

Единственное, что мне не нравится в этом решении, это то, что event имеет Task<string> возвращаемый тип, что странно. На самом деле уже наличие типа возврата для события является необычным. Лучшим решением было бы поместить свойство в обычай EventArgs что родной элемент управления будет установлен с результатом операции, но потому что InvokeScriptAsync метод асинхронный (и не асинхронный InvokeScript метод устарел и больше не должен использоваться) нам придется реализовать Task это будет завершено, когда свойство установлено. Такой подход используется в UWP с некоторыми событиями, они используют "отсрочку", которая говорит вызывающей стороне, что событие завершится только после завершения некоторой асинхронной операции. Я попытаюсь найти какой-нибудь авторитетный ответ о том, как вызывать собственную асинхронную операцию в случае пользовательских представлений:-) .

Другие вопросы по тегам