我有一个基本的UWP应用程序,内嵌WebView呈现相当大的HTML文档(最多500个字母大小的打印页面)。
我想添加对HTML文档打印的支持。这是我的方法:
<div style="height:100vh">
,最多500个。WebView
,我根据用户选择的页面大小调整大小以适合一页。scrollY
设置为仅使用JavaScript显示当前页面:window.scrollTo(0, -pageOffset)
WebViewBrush
捕获WebView
问题:
我可以生成所有500页的打印预览,但有时预览会丢失页面而其他页面会多次显示。
我怀疑这是因为我使用WebViewBrush.Redraw()
来捕获滚动的WebView的快照,但文档说Redraw()
异步发生。我可能会在WebViewBrush有机会重绘之前滚动浏览当前页面,因此会意外捕获下一页。
如何确保WebViewBrush已捕获WebView以便我可以滚动到下一页?
我的代码生成一个页面:
private async Task<Rectangle> MakePage(WebView webView,
Size pageSize, double pageOffset)
{
// Scroll to next page:
await webView.EvaluateJavaScriptSnippetAsync(
$"window.scrollTo(0, {pageOffset})");
var brush = new WebViewBrush();
brush.Stretch = Stretch.Uniform;
brush.SetSource(webView);
brush.Redraw(); // XXX Need to wait for this, but there's no API
// Add a delay hoping Redraw() finishes... I think here is the problem.
await Task.Delay(150);
var rectangle = new Rectangle()
{
Width = pageSize.Width,
Height = pageSize.Height
};
rectangle.Fill = brush;
brush.Stretch = Stretch.UniformToFill;
brush.AlignmentY = AlignmentY.Top;
return rectangle;
}
注意:如果有使用WebViewBrush打印500页的替代方法,我可以提供建议。我尝试为每个页面使用单独的WebView,但应用程序在200页后耗尽内存。
BOUNTY:我开始向任何可以弄清楚如何打印500页的人提供100分的赏金。
答案 0 :(得分:5)
根据WebViewBrush
remarks的重要部分:
WebView控件具有固有的异步行为,可在内容完全加载时重绘控件。但是一旦解析了XAML(可能是在WebView加载URI内容之前),关联的WebViewBrush就会呈现。或者,您可以等待在WebViewBrush上调用SetSource,直到源内容完全加载为止(例如,通过在WebView.LoadCompleted事件的处理程序中调用SetSource。
这样您就可以在WebView.LoadCompleted
之后调用SetSource
WebViewBrush
方法。
答案 1 :(得分:3)
我从您的问题中了解到,您只有一个HTML文档。这在内容方面非常大,您可以将其分为500页。您需要打印整个HTML页面。在这里,我认为HTML页面中没有您不想打印的内容或空格或部分。
我搜索了你的问题并得到了一些东西。我修改了该代码并在我的UWP应用程序上运行它,为您提供强大的基础。
我确信这不是您想要的确切解决方案,因为我不知道您要打印的页面大小和其他一些参数,但以下代码对您有90%以上的用处。
此代码根据您为方法提供的大小生成页面。它将把整个网页分成不。页面。它没有任何printng逻辑。我相信你有足够的能力(基于你的声誉点:))来玩C#和这段代码。您可以根据需要进行修改。
<强> XAML 强>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<WebView x:Name="wv" Source="http://www.stackoverflow.com"></WebView>
</Grid>
<强> C#强>
public MainPage()
{
this.InitializeComponent();
wv.LoadCompleted += Wv_LoadCompleted;
}
async private void Wv_LoadCompleted(object sender, NavigationEventArgs e)
{
var allPages = await GetWebPages(wv, new Windows.Foundation.Size(100d, 150d));
}
async Task<IEnumerable<FrameworkElement>> GetWebPages(WebView webView, Windows.Foundation.Size pageSize)
{
// GETTING WIDTH FROM WEVIEW CONTENT
var widthFromView = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
int contentWidth;
if (!int.TryParse(widthFromView, out contentWidth))
throw new Exception(string.Format("failure/width:{0}", widthFromView));
webView.Width = contentWidth;
// GETTING HEIGHT FROM WEBVIEW CONTENT
var heightFromView = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });
int contentHeight;
if (!int.TryParse(heightFromView, out contentHeight))
throw new Exception(string.Format("failure/height:{0}", heightFromView));
webView.Height = contentHeight;
// CALCULATING NO OF PAGES
var scale = pageSize.Width / contentWidth;
var scaledHeight = (contentHeight * scale);
var pageCount = (double)scaledHeight / pageSize.Height;
pageCount = pageCount + ((pageCount > (int)pageCount) ? 1 : 0);
// CREATE PAGES
var pages = new List<Windows.UI.Xaml.Shapes.Rectangle>();
for (int i = 0; i < (int)pageCount; i++)
{
var translateY = -pageSize.Height * i;
var page = new Windows.UI.Xaml.Shapes.Rectangle
{
Height = pageSize.Height,
Width = pageSize.Width,
Margin = new Thickness(5),
Tag = new TranslateTransform { Y = translateY },
};
page.Loaded += async (s, e) =>
{
var rectangle = s as Windows.UI.Xaml.Shapes.Rectangle;
var wvBrush = await GetWebViewBrush(webView);
wvBrush.Stretch = Stretch.UniformToFill;
wvBrush.AlignmentY = AlignmentY.Top;
wvBrush.Transform = rectangle.Tag as TranslateTransform;
rectangle.Fill = wvBrush;
};
pages.Add(page);
}
return pages;
}
async Task<WebViewBrush> GetWebViewBrush(WebView webView)
{
// ASSING ORIGINAL CONTENT WIDTH
var originalWidth = webView.Width;
var widthFromView = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
int contentWidth;
if (!int.TryParse(widthFromView, out contentWidth))
throw new Exception(string.Format("failure/width:{0}", widthFromView));
webView.Width = contentWidth;
// ASSINGING ORIGINAL CONTENT HEIGHT
var originalHeight = webView.Height;
var heightFromView = await webView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });
int contentHeight;
if (!int.TryParse(heightFromView, out contentHeight))
throw new Exception(string.Format("failure/height:{0}", heightFromView));
webView.Height = contentHeight;
// CREATING BRUSH
var originalVisibilty = webView.Visibility;
webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
var wvBrush = new WebViewBrush
{
SourceName = webView.Name,
Stretch = Stretch.Uniform
};
wvBrush.Redraw();
webView.Width = originalWidth;
webView.Height = originalHeight;
webView.Visibility = originalVisibilty;
return wvBrush;
}
答案 2 :(得分:0)
按承诺重新访问。原始答案保留在末尾。
原来比我想的要困难。
以下是一种完全不同的解决方案,可以完全避免该问题:https://github.com/ArthurHub/HTML-Renderer/
该项目被放弃,但状态良好,并且仍然有意义。有一个核心渲染器和许多针对各种.NET平台的包装器项目
未列出UWP,但核心渲染器是纯C#解决方案,有意排除了与平台相关的内容,并复制了几个完全可用的PAL(平台适配器层)。
我已使用它从MVC5 Web API进行打印,该API为SPA客户端提供了浏览器内网络打印服务。该库非常快,比MSHTML或EdgeHTML小很多。它不打印,而是渲染。这些示例呈现一个位图,打印示例将其绘制在WinForms PrintDocument.PrintPage事件提供的Graphics对象上。这将产生可怕的上采样像素化,(完全没有记录在案)解决方案是渲染Windows图元文件。分页问题仍然存在,但是有一个PDF的PAL,您可以从中使用该方法。
著名的遗言:应该很难将WPF解决方案转换为UWP。
如果您有兴趣,我们可以分叉存储库并添加缺少的UWP PAL。这样做的风险是要获得支持,因为它对猴子小部件很有用。
对于预览方面,您可能会在UI上对图元文件进行分页和绘制。
我首先想到的是一个应该起作用的丑陋的hack:休眠线程。重绘已经在另一个线程上发生,这就是为什么您处于竞争状态。如果您保证其他线程有更多时间,您的机会就会大大提高。
新问题:Thread.Sleep在UWP中不可用,因为除了不支持时,所有内容都支持延续。
幸运的是,还有其他方法可以给这只猫蒙皮。
wvBrush.Redraw();
// System.Threading.Tasks.Task.Delay(30).Wait() is more elegant but less portable than...
using (var waitHandle = new System.Threading.ManualResetEventSlim(initialState: false))
waitHandle.Wait(TimeSpan.FromMilliseconds(30)); // exploit timeout
下一个问题是渲染时间不可预测,长时间睡眠会损害性能,因此您需要一种测试成功的方法。
您说失败会产生一个全白页。我将为全白写一个测试,然后在我快速工作时返回并重写此答案的结尾。