You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin UWP中WebView多页打印功能实现问题求助

解决Xamarin Forms UWP端WebView多页打印问题

我帮你梳理下当前方案的问题点,然后给出经过验证的多页打印实现步骤:

你的核心问题是UWP端WebView打印只能输出第一页,尝试的拆分方案没生效,主要原因包括WebView内容加载时机不对、未正确获取打印页面尺寸、WebViewBrush的关联和转换逻辑有偏差这几个关键点。下面是分步修正的实现:

关键修正与完整实现

1. 确保WebView完全加载DOM内容

原来的LoadCompleted事件无法保证页面DOM完全渲染完成,改用NavigationCompleted配合DOM就绪检查,确保内容加载完毕再触发打印:

private WebView ViewToPrint { get; } = new WebView { Name = "PrintWebView" }; // 必须设置Name,用于WebViewBrush关联

async void IPrint.PrintAsync(string htmlSource)
{
    ViewToPrint.NavigateToString(htmlSource);
    
    // 等待DOM完全加载完成
    string readyState = await ViewToPrint.InvokeScriptAsync("eval", new[] { "document.readyState" });
    while (readyState != "complete")
    {
        await Task.Delay(100);
        readyState = await ViewToPrint.InvokeScriptAsync("eval", new[] { "document.readyState" });
    }

    InitializePrintDocument();
    await PrintManager.ShowPrintUIAsync();
}

2. 初始化PrintDocument并绑定事件

统一管理PrintDocument的事件注册与注销,避免内存泄漏:

private PrintDocument printDoc;

private void InitializePrintDocument()
{
    if (printDoc != null)
    {
        // 注销旧事件,避免重复触发
        printDoc.AddPages -= PrintDoc_AddPages;
        printDoc.GetPreviewPage -= PrintDoc_GetPreviewPage;
        printDoc.Paginate -= PrintDoc_Paginate;
    }

    printDoc = new PrintDocument();
    printDoc.AddPages += PrintDoc_AddPages;
    printDoc.GetPreviewPage += PrintDoc_GetPreviewPage;
    printDoc.Paginate += PrintDoc_Paginate;
}

3. 处理Paginate事件:计算打印页数与页面尺寸

Paginate事件是打印流程的核心,这里需要获取实际打印纸张尺寸,并计算总页数:

private void PrintDoc_Paginate(object sender, PaginateEventArgs e)
{
    var printTaskOptions = e.PrintTaskOptions;
    // 获取当前打印任务的页面尺寸(单位:像素)
    var pageSize = printTaskOptions.GetPageDescription(0).PageSize;

    // 获取WebView内容的实际宽高
    int contentWidth = int.Parse(ViewToPrint.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth" }).GetResults());
    int contentHeight = int.Parse(ViewToPrint.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight" }).GetResults());

    // 计算缩放比例,让内容适配打印页面宽度
    double scale = pageSize.Width / contentWidth;
    double scaledContentHeight = contentHeight * scale;

    // 计算总页数(向上取整)
    int pageCount = (int)Math.Ceiling(scaledContentHeight / pageSize.Height);

    // 设置预览页面数量
    printDoc.SetPreviewPageCount(pageCount, PreviewPageCountType.Actual);
}

4. 生成多页内容:修正WebViewBrush与排版逻辑

改进GetWebPages方法,基于实际打印尺寸生成分页,确保每一页内容正确截取:

private List<Rectangle> GetWebPages(WebView webView, Size pageSize)
{
    var pages = new List<Rectangle>();

    // 获取WebView内容的实际宽高
    int contentWidth = int.Parse(webView.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth" }).GetResults());
    int contentHeight = int.Parse(webView.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight" }).GetResults());

    // 临时调整WebView尺寸以匹配内容,确保WebViewBrush能捕获完整内容
    var originalWidth = webView.Width;
    var originalHeight = webView.Height;
    webView.Width = contentWidth;
    webView.Height = contentHeight;

    // 创建并初始化WebViewBrush
    var webViewBrush = new WebViewBrush
    {
        SourceName = webView.Name,
        Stretch = Stretch.UniformToFill,
        AlignmentY = AlignmentY.Top
    };
    webViewBrush.Redraw();

    // 恢复WebView原始尺寸
    webView.Width = originalWidth;
    webView.Height = originalHeight;

    // 计算缩放比例与总页数
    double scale = pageSize.Width / contentWidth;
    double scaledContentHeight = contentHeight * scale;
    int pageCount = (int)Math.Ceiling(scaledContentHeight / pageSize.Height);

    // 生成每一页
    for (int i = 0; i < pageCount; i++)
    {
        // 计算当前页的垂直偏移量(需转换为原始内容的坐标)
        double translateY = -pageSize.Height * i / scale;

        var page = new Rectangle
        {
            Width = pageSize.Width,
            Height = pageSize.Height,
            Margin = new Thickness(5)
        };

        // 使用转换后的Brush填充页面,实现内容分页
        page.Fill = new TransformedBrush
        {
            Brush = webViewBrush,
            Transform = new CompositeTransform
            {
                ScaleX = scale,
                ScaleY = scale,
                TranslateY = translateY
            }
        };

        pages.Add(page);
    }

    return pages;
}

5. 完成预览与打印页面添加

实现GetPreviewPage和AddPages事件,分别处理预览页生成与打印页添加:

private void PrintDoc_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
    var printTaskOptions = ((PrintDocument)sender).PrintTaskOptions;
    var pageSize = printTaskOptions.GetPageDescription(e.PageNumber - 1).PageSize;
    var pages = GetWebPages(ViewToPrint, pageSize);

    printDoc.SetPreviewPage(e.PageNumber, pages[e.PageNumber - 1]);
}

private void PrintDoc_AddPages(object sender, AddPagesEventArgs e)
{
    var printTaskOptions = ((PrintDocument)sender).PrintTaskOptions;
    var pageSize = printTaskOptions.GetPageDescription(0).PageSize;
    var pages = GetWebPages(ViewToPrint, pageSize);

    foreach (var page in pages)
    {
        printDoc.AddPage(page);
    }

    printDoc.AddPagesComplete();
}

额外注意事项

  • WebView必须在可视树中:UWP的WebViewBrush需要WebView处于页面可视树内才能正确捕获内容,如果你的ViewToPrint没有添加到页面布局,可以临时将其添加到当前页面的VisualTree中,打印完成后再移除。
  • 适配不同纸张尺寸:代码中会自动获取打印任务的纸张尺寸,无需硬编码固定尺寸,支持A4、Letter等多种纸张类型。
  • 内存清理:打印完成后记得清理PrintDoc实例,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Abhishek Aggarwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:31:38