首页 > 解决方案 > UWP WebView 打印显示打印页面模糊

问题描述

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

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

WebView 中使用的网站维基百科

参考WebView 到 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

打印的 PDF :在此处可用

更新

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

  1. 使用来自此处的事件PrintWebView()的代码创建了一个方法调用OnPrintButtonClick()
  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. 当应用程序在默认模式下运行时,它显示计数为allpages7 并且上述foreach调用PrintWebView()7 次。如果我将应用程序屏幕置于最大模式,则计数为allpages3,foreach调用PrintWebView()次数为 3 次(如预期的那样)。在这两种情况下,我都期望循环的最后一次迭代将带来打印对话框并打印所有页面 7(或 3,取决于应用程序屏幕的默认或最大模式)。相反,调试器转到方法的catch块,PrintWebView()如前所述,该方法的代码取自此处
  2. 问题:我是否正确遵循了上述步骤?如果没有,有什么更好的方法呢?

标签: c#webviewuwpwindows-community-toolkit

解决方案


当你将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;
}

推荐阅读