Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for WebViewBrush.Redraw() to finish (UWP printing)?

I have a basic UWP app with an embedded WebView presenting a rather large HTML document (up to 500 letter-sized printed pages).

I'd like to add support for printing that HTML document. Here is my approach:

  1. To support pagination, I generate a second HTML document broken into "pages" by using a <div style="height:100vh"> for each "page", up to 500 of these.
  2. I load that "paginated" HTML into a second, hidden WebView on the XAML page that I resize to fit exactly one page based on the user selected page size.
  3. I wait for the WebView to finish loading...
  4. Now for each "page":
    1. I set the WebView's scrollY to show only the current page using JavaScript: window.scrollTo(0, -pageOffset)
    2. I then use WebViewBrush to capture a snapshot of the current page in the WebView
    3. Repeat this for all remaining pages...

The problem:

I can generate the print preview of all 500 pages, but sometimes the preview is missing pages while other pages show up multiple times.

I suspect this is because I use WebViewBrush.Redraw() to capture a snapshot of the scrolled WebView, but the documentation says Redraw() happens asynchronously. I may scroll past the current page before WebViewBrush gets a chance to redraw, hence accidentally capturing the next page.

How can I make sure that WebViewBrush has captured the WebView so that I can scroll to the next page?

My code to generate a single page:

    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;
    }

Note: If there's an alternative to using WebViewBrush to print 500 pages, I'm open for suggestions. I tried using a separate WebView for each page, but the app runs out of memory after 200 pages.

BOUNTY: I started a bounty offering 100 points to anybody who can figure out how to print 500 pages.

like image 999
Mark Avatar asked Mar 21 '18 16:03

Mark


1 Answers

According to the Important section of the WebViewBrush remarks:

A WebView control has an inherently asynchronous behavior that redraws the control when its content is completely loaded. But an associated WebViewBrush renders as soon as the XAML is parsed (which might be before the URI content is loaded by the WebView ). Alternatively, you can wait to call SetSource on the WebViewBrush until the source content is fully loaded (for example by calling SetSource in the handler for the WebView.LoadCompleted event.

So that you could calling SetSource method of WebViewBrush after WebView.LoadCompleted.

like image 156
Sunteen Wu Avatar answered Oct 13 '22 21:10

Sunteen Wu