将 XAML 转换为 PDF 并为 Xamarin.Forms UWP 项目对其进行分页

如何解决将 XAML 转换为 PDF 并为 Xamarin.Forms UWP 项目对其进行分页

直到最近,在我不知何故退出 Dev Limbo 的项目中,我一直坚持如何实现将报告从 StackLayout“导出”到 PDF 的目标。

--背景故事--

以前,我尝试继续使用已放置(在项目中)PDFSharp 包将 XAML 中显示的数据转换为客户端的 PDF。长话短说,我无法让 PDFSharp 做我需要它做的事情,于是转向 Syncfusion。它们似乎具有实现这一目标所需的功能。根据他们拥有的代码示例,我能够接近我的目标,但还不够。它们有捕获部分和分页部分,但不是两者的组合。我基本上需要对 CaptureAsync() 保存的屏幕截图进行分页,以制作整个报告的 pdf。

解决方法

--这是如何解决的?--

在进行了一些挖掘之后,我在这个 article 中找到了一个答案(我永远感激)并使用它制定了一个解决方案。

这是我的 XAML 内容页面的上下文示例:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:ReportTool.Controls"
             x:Class="ReportTool.ReportViewer">
    <ContentPage.Content>
        <StackLayout Style="{StaticResource TopLevelStackLayout}">

            <!-- Body Block -->
            <Grid x:Name="MainGrid"  Style="{StaticResource MainContainingGrid}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="1*" />
                </Grid.RowDefinitions>
                <ScrollView x:Name="MainScrollLayout" VerticalOptions="Fill" HorizontalOptions="Fill" Grid.Row="0" Grid.Column="0" BackgroundColor="#FFFFFF" MinimumWidthRequest="700">
                    <StackLayout x:Name="MainStackLayout" Style="{StaticResource MainBkg}">
                        
                        <Button x:Name="DownloadPdfBtn"  Text="Export to PDF" Clicked="DownloadPdfBtn_OnClicked" TextColor="White" BackgroundColor="DodgerBlue" VerticalOptions="Start" HorizontalOptions="Start" />

                        <Image Source="~\..\Assets\Logos\CompanyLogo.png" Margin="0,60,10" HorizontalOptions="Center" />
                        <Label x:Name="TitlePageTitleText" Style="{StaticResource ReportViewerTitleTextMain}" Text="{StaticResource CompanyAnalysisReport}" />
                        <Label x:Name="TitlePagePreparedFor" Style="{StaticResource ReportViewerTitleTextMiddle}" Text="{StaticResource PreparedFor}" />
                        <Label x:Name="TitlePageOrganizationName" Style="{StaticResource ReportViewerTitleTextMiddle}" />
                        <Label x:Name="TitlePageOrganizationAddress1" Style="{StaticResource ReportViewerTitleTextMiddle}" />
                        <Label x:Name="TitlePageOrganizationAddress2" Style="{StaticResource ReportViewerTitleTextMiddle}" />
                        <Label x:Name="TitlePageOrganizationCityStateZip" Style="{StaticResource ReportViewerTitleTextLast}" />

                        <Grid x:Name="ReportGrid" Style="{StaticResource ReportGridBody}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="90"/>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="125"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="1*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                        </Grid>

                    </StackLayout>
                </ScrollView>
            </Grid>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

这是节目明星的代码,ExportToPdf 按钮:

using Syncfusion.Drawing;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;

private async void DownloadPdfBtn_OnClicked(object sender,EventArgs e)
{
  try
  {
     var filename = "SurveyReport_" + ((App)Application.Current).CurrentUser.UserName + "_" + DateTime.UtcNow.ToString("MMddyy") + ".pdf";
                
     // Init Memory Stream.
     var stream = new MemoryStream();
                
     //Create a new PDF document
     using (var document = new PdfDocument())
     {
       // Add page to the PDF document.
       var page = document.Pages.Add();

      // Get the scroll view height.
      var xamlPageHeight = MainScrollLayout.ContentSize.Height;

      // Get the page dimensions.
      var pageWidth = page.GetClientSize().Width;
      var pageHeight = page.GetClientSize().Height;

      // Capture the number of pages.
      var numberOfPages = (int)Math.Ceiling(xamlPageHeight / pageHeight);
                    
      for (var i = 0; i < numberOfPages; i++)
      {
        // Find beginning of page.
        await MainScrollLayout.ScrollToAsync(0,i * pageHeight,false).ConfigureAwait(false);

        // Capture the XAML page as an image and returns the image in memory stream.
        var byteData = await DependencyService.Get<IExportPdf>().CaptureAsync();
        var imageStream = new MemoryStream(byteData);
                    
        // Load the image in PdfBitmap.
        var pdfBitmapImage = new PdfBitmap(imageStream);

        // Set the pdf page settings.
        document.PageSettings.Margins.All = 0;
        document.PageSettings.Orientation = PdfPageOrientation.Portrait;
        document.PageSettings.Size = new SizeF(pageWidth,pageHeight);
                        
        // Add new page for graphics (otherwise graphics won't know where to draw the rest of the image)
        page = document.Pages.Add();

        // Graphics for drawing image to pdf.
        var graphics = page.Graphics;

        // Draw the image to the page.
        graphics.DrawImage(pdfBitmapImage,pageWidth,pageHeight);

        // Insert page at i position.
        document.Pages.Insert(i,page);

        // Save the document into memory stream.
        document.Save(stream);
      }
     }
                    
     stream.Position = 0;

     // Save the stream as a file in the device and invoke it for viewing.
     await Xamarin.Forms.DependencyService.Get<IExportPdf>().Save(filename,"application/pdf",stream);
    }
    catch (Exception ex)
    {
        DisplayErrorAlert("DownloadPdfBtn_OnClicked",ex.StackTrace);
    }
}

需要注意的是,为了保存本地内存以外的任何地方,您需要一个依赖项。幸运的是,Syncfusion 提供了一个片段供您使用。为了您的时间,我将分享这些片段。您将需要添加两个 .cs 文件,一个具有捕获/保存功能的类文件和一个用于您的应用的接口文件。

捕获/保存类:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Graphics.Display;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Imaging;
using Xamarin.Forms;

public class ExportPdf : IExportPdf
{
    public async Task<byte[]> CaptureAsync()
    {
        var renderTargetBitmap = new RenderTargetBitmap();
        await renderTargetBitmap.RenderAsync(Window.Current.Content);
            
        var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
        var pixels = pixelBuffer.ToArray();
            
        var displayInformation = DisplayInformation.GetForCurrentView().LogicalDpi;
            
        var stream = new InMemoryRandomAccessStream();
            
        var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId,stream);
        encoder.SetPixelData(
            BitmapPixelFormat.Bgra8,BitmapAlphaMode.Ignore,(uint)renderTargetBitmap.PixelWidth,(uint)renderTargetBitmap.PixelHeight,displayInformation,pixels);
        await encoder.FlushAsync();
            
        stream.Seek(0);
        var readStream = stream.AsStreamForRead();
        var bytes = new byte[readStream.Length];
        await readStream.ReadAsync(bytes,bytes.Length);

        return bytes;
    }

    public async Task Save(string filename,string contentType,MemoryStream stream)
    {
        if (Device.Idiom != TargetIdiom.Desktop)
        {
            var local = ApplicationData.Current.LocalFolder;
            var outFile = await local.CreateFileAsync(filename,CreationCollisionOption.ReplaceExisting);
            using (var outStream = await outFile.OpenStreamForWriteAsync()) { await outStream.WriteAsync(stream.ToArray(),(int)stream.Length); }

            if (contentType != "application/html") await Windows.System.Launcher.LaunchFileAsync(outFile);
        }
        else
        {
            StorageFile storageFile = null;
            var savePicker = new FileSavePicker
            {
                SuggestedStartLocation = PickerLocationId.Desktop,SuggestedFileName = filename
            };
            switch (contentType)
            {
                case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
                    savePicker.FileTypeChoices.Add("PowerPoint Presentation",new List<string> { ".pptx" });

                    break;

                case "application/msexcel":
                        savePicker.FileTypeChoices.Add("Excel Files",new List<string> { ".xlsx" });

                    break;

                case "application/msword":
                        savePicker.FileTypeChoices.Add("Word Document",new List<string> { ".docx" });

                    break;

                case "application/pdf":
                        savePicker.FileTypeChoices.Add("Adobe PDF Document",new List<string> { ".pdf" });

                    break;
                case "application/html":
                        savePicker.FileTypeChoices.Add("HTML Files",new List<string> { ".html" });

                    break;
            }

            storageFile = await savePicker.PickSaveFileAsync();

            using (var outStream = await storageFile.OpenStreamForWriteAsync())
            {
                await outStream.WriteAsync(stream.ToArray(),(int)stream.Length);
                await outStream.FlushAsync();
                outStream.Dispose();
            }

            stream.Flush();
            stream.Dispose();
            await Windows.System.Launcher.LaunchFileAsync(storageFile);
        }
    }
}

界面:

using System.IO;
using System.Threading.Tasks;

public interface IExportPdf
{
    Task Save(string filename,MemoryStream stream);
    Task<byte[]> CaptureAsync();
}

那应该可以!我希望这可以帮助任何承担过类似任务的人!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res