首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何等待WebViewBrush.Redraw()完成(UWP打印)?

如何等待WebViewBrush.Redraw()完成(UWP打印)?
EN

Stack Overflow用户
提问于 2018-03-21 16:02:36
回答 3查看 911关注 0票数 10

我有一个基本的UWP应用程序,嵌入式WebView显示一个相当大的HTML文档(多达500个字母大小的打印页)。

我想添加对打印HTML文档的支持。以下是我的做法:

  1. 为了支持分页,我为每个“页面”使用一个<div style="height:100vh">来生成第二个被分解成“页面”的HTML文档,其中最多500个。
  2. 我将这个“分页的”HTML加载到XAML页面上的第二个隐藏的WebView中,然后根据用户选择的页面大小重新调整大小以适合一个页面。
  3. 我等待WebView完成装载.
  4. 现在对于每个“页面”:
    1. 我将WebView的scrollY设置为只显示使用JavaScript:window.scrollTo(0, -pageOffset)的当前页面
    2. 然后使用WebViewBrush捕获WebView中当前页面的快照。
    3. 在剩下的几页上重复这个..。

问题:

我可以生成所有500页的打印预览,但有时预览会丢失页面,而其他页面则会多次出现。

我怀疑这是因为我使用WebViewBrush.Redraw()来捕获滚动WebView的快照,但是文档中说Redraw() 是异步发生的。在WebViewBrush有机会重新绘制之前,我可能会翻阅当前页面,从而意外地捕获下一页。

如何确保WebViewBrush捕获了WebView,以便滚动到下一页?

生成一个页面的代码:

代码语言:javascript
复制
    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页之后耗尽了内存。

奖品:,我开始了一项赏金计划,每一个能打印500页的人都能得到100分。

EN

回答 3

Stack Overflow用户

发布于 2018-03-22 03:17:32

根据 remarks的重要部分

WebView控件具有固有的异步行为,当控件的内容被完全加载时,它会重新绘制控件。但是,相关的WebViewBrush在XAML被解析后立即呈现(这可能在WebView加载URI内容之前)。或者,您可以等待在SetSource上调用WebViewBrush,直到完全加载源内容(例如,在处理程序中调用WebView.LoadCompleted事件的SetSource )。

以便您可以在SetSource之后调用WebViewBrushWebView.LoadCompleted方法。

票数 5
EN

Stack Overflow用户

发布于 2018-03-26 06:37:30

我从你的问题中了解到,你只有一个HTML文档。这是非常大的内容,你可以把它划分成500页。你需要打印整个HTML页面。在这里,我认为HTML页面中没有不想打印的内容、空间或部分。

我搜索了你的问题发现了一些东西。我修改了这段代码,并在我的UWP应用程序上运行,为您提供了强大的基础。

我确信这不是您想要的确切解决方案,因为我不知道您想要打印的页面大小和其他参数,但是下面的代码对您是超过90%有用的。

此代码根据提供给方法的大小生成页。它将把整个网页划分为no。一页一页。它没有任何打印逻辑。我确信您有足够的能力(基于您的声誉点:)来处理C#和这段代码。您可以根据需要修改它。

XAML

代码语言:javascript
复制
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <WebView x:Name="wv" Source="http://www.stackoverflow.com"></WebView>
</Grid>

C#

代码语言:javascript
复制
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;
}
票数 3
EN

Stack Overflow用户

发布于 2018-08-30 06:17:43

重获承诺。原始答案保留在末尾.

比我想象的要难得多。

这里有一个完全不同的解决方案,可以完全避免这个问题:https://github.com/ArthurHub/HTML-Renderer/

这一项目已被放弃,但状况良好,仍然具有现实意义。有一个核心呈现器和一系列针对各种.NET平台的包装器项目。

  • WinForms
  • WPF
  • 单声道
  • dotnet核

UWP没有列出,但是核心呈现器是一个纯的C#解决方案,它故意将与平台相关的内容分解出来,并复制几个完全工作的伙伴(平台适配器层)。

我使用它从一个MVC5网络API打印,提供浏览器内的网络打印服务给SPA客户端。这个库速度快得惊人,比MSHTML或EdgeHTML小得多。它不打印,它呈现。这些示例呈现一个位图,打印示例在WinForms PrintDocument.PrintPage事件提供的Graphics对象上绘制位图。这会产生可怕的上采样像素化,而(完全没有文档记录的)解决方案是呈现一个Windows元文件。仍然存在分页的问题,但是有一个用于PDF的PAL,您可以从它中建立这个方法。

著名的遗言:将WPF解决方案转换为UWP应该不难。

如果你感兴趣,我们可以分叉回购和添加失踪的UWP伙伴。这样做的风险在于需要支持--它可能对小部件猴子非常有用。

对于预览方面,您可能会在UI上分页和绘制元文件。

我的第一个想法是一次丑陋的黑客攻击,这应该是可行的:好好睡一觉。重绘已经在另一个线程上进行,这就是为什么您处于竞争状态。如果你保证其他线程有更多的时间,你的机会会大大提高。

新问题: Thread.Sleep在UWP中是不可用的,因为所有的东西都支持延续,除非它不支持。

幸运的是,还有其他方法剥去这只猫的皮。

代码语言:javascript
复制
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

下一个问题是渲染时间不可预测,长时间睡眠会影响性能,因此您需要一种测试成功的方法。

你说失败会产生一个全白的页面。我会为所有的白色写一个测试,回来,并重写这个答案的结尾,当我使它工作得很快。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49411176

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档