在 Xamarin.Forms (UWP) 项目中,我有一个WebView
控件,其Source
是使用 HTML 字符串创建的,如下所示:
var webview = new Xamarin.Forms.WebView
{
Source = new HtmlWebViewSource
{
Html = "<html>....</html>"
}
};
HTML包含 JavaScript,可以在<body>
内动态生成 HTML。这将在屏幕上完美呈现。这意味着 WebView 理解使用 JavaScript 创建的 DOM。伟大。
但是现在我需要解析一些生成的 HTML,但我似乎只能访问我作为源代码传入的原始 HTML 字符串,而不是最终生成的 DOM。
有没有办法将JavaScript生成并被WebView理解的DOM转换为字符串,以便我可以解析(使用HTML Agility Pack或AngleSharp等库)并提取HTML的某些段?这可以是在Xamarin.Forms或UWP(我面向的平台)中。
注意:在完全披露的情况下(以防有帮助,并避免指责这是一个 XY 问题),我最终试图解决在 UWP 上打印具有多个页面的 Web 视图的问题 - 对此的研究遇到了非常稀疏的信息。我有一个适用于不是使用 JavaScript 动态生成的 HTML 的解决方案 - 基本上我正在提取代表可打印页面的 HTML 部分,并将它们添加为单独的页面进行打印和打印预览。但如前所述,我似乎无法解析动态生成的内容。
我的第一个想法是使用 Xamarin.Forms 中内置的Eval
方法,但后来我发现这种方法不返回任何内容,因此它仅适用于应用到 Web 视图的通信。
到目前为止,实现此目的的最简单方法是使用自定义版本的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;" });
}
}
}
诀窍在于我们调用JavaScripteval
函数,该函数将从Web视图返回HTML本身。
只需将 XAML 中的WebView
替换为我们的ExtendedWebView
,并在需要时调用其GetHtmlAsync
方法。
我唯一不喜欢这个解决方案的是event
Task<string>
返回类型,这很奇怪。实际上,事件上已经具有返回类型是不寻常的。更好的解决方案是将一个属性放在本机控件将使用操作结果设置的自定义EventArgs
中,但由于InvokeScriptAsync
方法是异步的(并且非异步InvokeScript
方法已过时,不应再使用),因此我们必须实现在设置属性时完成的自定义Task
。这种方法在某些事件的 UWP 中使用,它们使用"延迟",该"延迟"表示调用方事件仅在某些异步操作完成后才会完成。我将尝试寻找一些关于在自定义视图的情况下应如何实现调用本机异步操作的权威答案:-)。