【问题标题】:UWP WebView Printing shows the printed page blurredUWP WebView 打印显示打印页面模糊
【发布时间】:2020-10-23 08:57:05
【问题描述】:

网上有很多投诉(如this 一个和this 一个)关于打印WebVeiw 的网页,打印的页面只显示屏幕上的当前视图,而不是整个页面。通过浏览在线搜索中的一些代码,我能够使用以下代码打印整个页面。但是打印的页面已尝试在启用滚动条的情况下将其全部打印在一个页面中。而且打印出来的内容很模糊(如下图)。

问题:在启用滚动条的情况下,如何在打印整个页面的同时使打印的内容不模糊?

WebView 中使用的网站Wikipedia

参考WebView to WebViewBrush

MainPage.xaml

<Page
    x:Class="UWP_WebViewPrinting.MainPage"
    ....>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <WebView x:Name="wvTest" Source="https://en.wikipedia.org/wiki/Universal_Windows_Platform" Grid.Row="1" Margin="0,61,0,0"/>
        <Button x:Name="btnTest" Content="Test" Grid.Row="0"  VerticalAlignment="Top" Click="btnTest_Click"/>
        <Rectangle x:Name="RectangleToPrint" Grid.Row="1"/>
    </Grid>
</Page>

MainPage.xaml.cs

private async void btnTest_Click(object sender, RoutedEventArgs e)
{
    //Step 1: use WebViewBrush to render the content of webview into the Rectangle
    int width;
    int height;
    // get the total width and height
    var widthString = await wvTest.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
    var heightString = await wvTest.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });

    if (!int.TryParse(widthString, out width))
    {
        throw new Exception("Unable to get page width");
    }
    if (!int.TryParse(heightString, out height))
    {
        throw new Exception("Unable to get page height");
    }

    // resize the webview to the content
    wvTest.Width = width;
    wvTest.Height = height;

    WebViewBrush b = new WebViewBrush();
    b.SourceName = "wvTest";
    b.Redraw();
    RectangleToPrint.Fill = b;

    //Step 2: Then print the rectangle
    if (PrintManager.IsSupported())
    {
        try
        {
            // Show print UI
            await PrintManager.ShowPrintUIAsync();
        }
        catch
        {
            // Printing cannot proceed at this time
            ContentDialog noPrintingDialog = new ContentDialog()
            {
                Title = "Printing error",
                Content = "\nSorry, printing can' t proceed at this time.",
                PrimaryButtonText = "OK"
            };
            await noPrintingDialog.ShowAsync();
        }
    }
    else
    {
        // Printing is not supported on this device
        ContentDialog noPrintingDialog = new ContentDialog()
        {
            Title = "Printing not supported",
            Content = "\nSorry, printing is not supported on this device.",
            PrimaryButtonText = "OK"
        };
        await noPrintingDialog.ShowAsync();
    }
}

#region Register for printing

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // Register for PrintTaskRequested event
    printMan = PrintManager.GetForCurrentView();
    printMan.PrintTaskRequested += PrintTaskRequested;

    // Build a PrintDocument and register for callbacks
    printDoc = new PrintDocument();
    printDocSource = printDoc.DocumentSource;
    printDoc.Paginate += Paginate;
    printDoc.GetPreviewPage += GetPreviewPage;
    printDoc.AddPages += AddPages;
}

#endregion

#region Showing the print dialog

private void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
    // Create the PrintTask.
    // Defines the title and delegate for PrintTaskSourceRequested
    var printTask = args.Request.CreatePrintTask("Print", PrintTaskSourceRequrested);

    // Handle PrintTask.Completed to catch failed print jobs
    printTask.Completed += PrintTaskCompleted;
}

private void PrintTaskSourceRequrested(PrintTaskSourceRequestedArgs args)
{
    // Set the document source.
    args.SetSource(printDocSource);
}

#endregion

#region Print preview

private void Paginate(object sender, PaginateEventArgs e)
{
    // As I only want to print one Rectangle, so I set the count to 1
    printDoc.SetPreviewPageCount(1, PreviewPageCountType.Final);
}

private void GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
    // Provide a UIElement as the print preview.
    printDoc.SetPreviewPage(e.PageNumber, this.RectangleToPrint);
}

#endregion

#region Add pages to send to the printer

private void AddPages(object sender, AddPagesEventArgs e)
{
    printDoc.AddPage(this.RectangleToPrint);

    // Indicate that all of the print pages have been provided
    printDoc.AddPagesComplete();
}

#endregion

#region Print task completed

private async void PrintTaskCompleted(PrintTask sender, PrintTaskCompletedEventArgs args)
{
    // Notify the user when the print operation fails.
    if (args.Completion == PrintTaskCompletion.Failed)
    {
        await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
        {
            ContentDialog noPrintingDialog = new ContentDialog()
            {
                Title = "Printing error",
                Content = "\nSorry, failed to print.",
                PrimaryButtonText = "OK"
            };
            await noPrintingDialog.ShowAsync();
        });
    }
}

#endregion

印刷版 PDFhere

更新

@Faywang - MSFT 的回复似乎很有希望。我尝试如下:

  1. 使用来自hereOnPrintButtonClick() 事件的代码创建了一个方法调用PrintWebView()
  2. 添加了我上面帖子中显示的所有其他方法/事件。
  3. 在我的btnTest_Click(...)中,我修改了用户@Faywang的代码如下

.

List<Rectangle> allpages = await GetWebPages(wvTest, new Windows.Foundation.Size(750d, 950d));
//print these pages
foreach (Rectangle rectangle in allpages)
   PrintWebView();
  1. 当应用程序在默认模式下运行时,它显示 allpages 的计数为 7 并且上述 foreach 调用 PrintWebView() 7 次。如果我将应用程序屏幕置于最大模式,allpages 的计数为 3,foreach 调用PrintWebView() 3 次(如预期的那样)。在这两种情况下,我都期望循环的最后一次迭代将带来打印对话框并打印所有页面 7(或 3,取决于应用程序屏幕的默认或最大模式)。相反,调试器转到PrintWebView() 方法的catch 块,如前所述,其代码取自here
  2. 问题:我是否正确执行了上述步骤?如果没有,有什么更好的方法?

【问题讨论】:

    标签: c# webview uwp windows-community-toolkit


    【解决方案1】:

    当你把webview压缩重绘成矩形时,会变得模糊,最好多页显示webview。首先我给打印页面的宽度和高度为750、950,然后根据比例计算需要多少页。之后,您可以打印这些矩形。例如:

    private async void btnTest_Click(object sender, RoutedEventArgs e)
    {
        allPages = await GetWebPages(wvTest, new Windows.Foundation.Size(750d, 950d));
        //print these pages
    }
    
    async Task<List<Windows.UI.Xaml.Shapes.Rectangle>> GetWebPages(Windows.UI.Xaml.Controls.WebView webView, Windows.Foundation.Size page)
    {
        // ask the content its width
        var _WidthString = await webView.InvokeScriptAsync("eval",
                    new[] { "document.body.scrollWidth.toString()" });
        int _ContentWidth;
        if (!int.TryParse(_WidthString, out _ContentWidth))
            throw new Exception(string.Format("failure/width:{0}", _WidthString));
        webView.Width = _ContentWidth;
    
        // ask the content its height
        var _HeightString = await webView.InvokeScriptAsync("eval",
                    new[] { "document.body.scrollHeight.toString()" });
        int _ContentHeight;
        if (!int.TryParse(_HeightString, out _ContentHeight))
            throw new Exception(string.Format("failure/height:{0}", _HeightString));
        webView.Height = _ContentHeight;
    
        // how many pages will there be?
        var _Scale = page.Width / _ContentWidth;
        var _ScaledHeight = (_ContentHeight * _Scale);
        var _PageCount = (double)_ScaledHeight / page.Height;
        _PageCount = _PageCount + ((_PageCount > (int)_PageCount) ? 1 : 0);
    
        // create the pages
        var _Pages = new List<Windows.UI.Xaml.Shapes.Rectangle>();
        for (int i = 0; i < (int)_PageCount; i++)
        {
            var _TranslateY = -page.Height * i;
            var _Page = new Windows.UI.Xaml.Shapes.Rectangle
            {
                Height = page.Height,
                Width = page.Width,
                Margin = new Windows.UI.Xaml.Thickness(5),
                Tag = new Windows.UI.Xaml.Media.TranslateTransform { Y = _TranslateY },
            };
            _Page.Loaded += async (s, e) =>
            {
                var _Rectangle = s as Windows.UI.Xaml.Shapes.Rectangle;
                var _Brush = await GetWebViewBrush(webView);
                _Brush.Stretch = Windows.UI.Xaml.Media.Stretch.UniformToFill;
                _Brush.AlignmentY = Windows.UI.Xaml.Media.AlignmentY.Top;
                _Brush.Transform = _Rectangle.Tag as Windows.UI.Xaml.Media.TranslateTransform;
                _Rectangle.Fill = _Brush;
            };
            _Pages.Add(_Page);
        }
        return _Pages;
    }
    
    async Task<WebViewBrush> GetWebViewBrush(Windows.UI.Xaml.Controls.WebView webView)
    {
        // resize width to content
        var _OriginalWidth = webView.Width;
        var _WidthString = await webView.InvokeScriptAsync("eval",
                new[] { "document.body.scrollWidth.toString()" });
        int _ContentWidth;
        if (!int.TryParse(_WidthString, out _ContentWidth))
                throw new Exception(string.Format("failure/width:{0}", _WidthString));
        webView.Width = _ContentWidth;
    
        // resize height to content
        var _OriginalHeight = webView.Height;
        var _HeightString = await webView.InvokeScriptAsync("eval",
                new[] { "document.body.scrollHeight.toString()" });
        int _ContentHeight;
        if (!int.TryParse(_HeightString, out _ContentHeight))
                throw new Exception(string.Format("failure/height:{0}", _HeightString));
        webView.Height = _ContentHeight;
    
        // create brush
        var _OriginalVisibilty = webView.Visibility;
        webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
        var _Brush = new WebViewBrush
        {
            SourceName = webView.Name,
            Stretch = Windows.UI.Xaml.Media.Stretch.Uniform
        };
        _Brush.Redraw();
    
        // reset, return
        webView.Width = _OriginalWidth;
        webView.Height = _OriginalHeight;
        webView.Visibility = _OriginalVisibilty;
        return _Brush;
    }
    

    【讨论】:

    • 您的回答似乎很有说服力。但我无法打印任何页面。我添加了一个 UPDATE 部分来描述我是如何遵循您的建议的。如果你有时间,你介意让我知道我可能做错了什么以及应该如何正确完成吗?
    • 为简单起见,您可以尝试使用Print Helper 打印多个矩形。将 Rectangle 放入 AddFrameworkElementToPrint() 方法并打印它。或者你可以参考这个official sample打印出来。
    • 按照您的指示,我使用Print Helper 如下:List&lt;Rectangle&gt; _allPages = await GetWebPages(wvTest, new Size(750d, 950d)); for (int i = 0; i &lt; _allPages.Count; i++) { printHelper.AddFrameworkElementToPrint(_allPages[i]); etc. etc. 这次打印效果更好。但是分页并没有完全对齐。 PDF 为here。我们如何纠正分页对齐,有什么建议吗? }
    • 我尝试创建一个StackPanel并在其中添加Rectangle,然后将StackPanel传递给AddFrameworkElementToPrint()方法,它可以很好地打印。你也可以试试。另外我创建了一个很简单的sample,大家可以下载查看。
    • 您的样本效果更好。您的示例中似乎有一个小错字,您可能需要对其进行编辑。您可能希望 container_printHelper = new PrintHelper(Container); 中带有小 c,因为另一个带有大写 CContainer 不包含 WebView 控件,因此所有页面都打印为空白。
    猜你喜欢
    • 2018-11-01
    • 2011-03-21
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-11
    相关资源
    最近更新 更多