WPF中打印/报告的最佳方法是什么?

72

我即将要开始一个项目,需要从数据中打印简单报告。它将基于WPF进行开发,我在考虑应该选择哪种方式。

我知道WPF引入了自己的打印技术(基于XPS),看起来很容易使用。然而,我想知道是否使用ReportViewer控件并将其嵌入到Windows Forms宿主控件中会更容易,因为这将使用户能够导出各种格式以及打印。

有没有人从WPF中打印/报告的经验?你推荐哪个方向?


SimpleWPFReporting提供了使用WPF XAML的全部功能来创建任何报告的能力。SimpleWPFReporting将负责将其导出为PDF或打印。 - Max
8个回答

43

RDL 的限制

最初我选择了 RDLC/ReportViewer 来进行 WPF 打印,但发现它的限制非常大。以下是我发现的一些限制:

  • RDL 只能创建最单调的报表
  • 使用 RDL 创建报表比在 WPF 中直接创建更费力:设计工具与 Expression Blend 相比非常原始,并且 RDL 只处理表格
  • 我不能使用 ControlTemplates、DataTemplates、Styles 等功能
  • 我的报表字段和列无法根据数据大小有效地调整大小和重新排列
  • 图形必须导入为图像 - 不能绘制或编辑矢量图形
  • 项目定位需要编写代码而不是数据绑定
  • 缺乏变换
  • 非常基本的数据绑定

直接从 WPF 进行打印非常简单

由于这些限制,我尝试使用纯 WPF 创建报表,并发现这实际上非常容易。WPF 允许您实现自己的 DocumentPaginator 子类来生成页面。

我开发了一个简单的 DocumentPaginator 子类,它可以获取任何 Visual,分析可视树,并隐藏选定的元素以创建每个页面。

DocumentPaginator 详细信息

以下是我的 DocumentPaginator 子类在初始化期间(第一次获取 PageCount 或第一次调用 GetPage() 时)执行的操作:

  1. 扫描可视树,并创建 ItemsControls 内部所有滚动面板的映射
  2. 从外到内逐步减少 ItemsControls 中的项目,直到 Visual 可以放在单个页面上而无需滚动。如果最外层无法进一步减少,则减少内部面板,直到成功或每个级别只有一个项目。将可见项目集合记录为第一页。
  3. 显示已经出现在第一页的最底层项目,然后显示以前隐藏的项目,直到它们不能再放在页面上为止。记录所有但最后添加的项目为第二页。
  • 重复此过程以获取所有页面,并将结果存储在数据结构中。
  • 我的DocumentPaginator的GetPage方法如下:

    1. 在初始化期间生成的数据结构中查找给定的页面编号
    2. 根据数据结构中的指示隐藏和显示可视树中的项目
    3. 设置PageNumber和NumberOfPages附加属性,以便报告可以显示页码
    4. 刷新Dispatcher (Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => {} ));) 以使任何后台呈现任务完成
    5. 创建一个大小为页面的矩形,其VisualBrush是正在打印的可视内容
    6. 测量、排列和更新布局矩形,然后返回它

    这是相当简单的代码,让我能够将几乎任何我可以使用WPF创建的东西变成页面并打印出来。

    额外的报告支持

    既然我的分页器已经工作了,我就不用再担心我是否正在为屏幕或纸张创建我的WPF内容了。事实上,我建立的用于数据输入和编辑的UI通常也非常适合打印。

    从那里开始,我添加了一个简单的工具栏和一些代码,结果建立了一个完整的基于WPF的报告系统,比RDL要强大得多。我的报告代码可以导出文件,打印到打印机,剪切/粘贴页面图像以及剪切/粘贴Excel数据。我还可以通过单击复选框将任何UI切换为“打印视图”,以查看它在打印时的外观。所有这些只用了几百行C#和XAML代码!

    目前为止,我认为RDL唯一具有而我的报告代码没有的功能是生成格式化的Excel电子表格的能力。我可以看出如何做到这一点,但迄今为止还没有必要-仅剪切和粘贴数据已经足够。

    从我的经验来看,我的建议是编写一个分页器,然后开始使用WPF本身来创建您的报告。


    7
    很有趣,因为我正在考虑放弃 RDLC。你是否已经发布了你的文档分页器代码? - Eduardo Molteni
    2
    你能否进一步阐述一下?也许可以提供一些简短的代码/XAML示例吗? - Alex Hope O'Connor
    7
    “直接从WPF打印非常容易。”真的吗?我已经花了很多时间和精力来实现这一点,对于数据绑定的数据网格进行分页处理并不容易。恐怕魔鬼就在细节中,除非我看到一些细节解决方案,否则这个答案并不是非常有用的。 - Manos Dilaverakis
    1
    在这里,@RayBurns。让我们看看一个 Github 存储库,以便我们可以帮助贡献这个听起来很棒的实现。 - Killnine
    1
    有趣的是,距离这个答案已经过去6年了,仍然找不到任何帮助我将xaml视图转换为页面的class。而且,没有其他方法 :( - Gopichandar
    显示剩余5条评论

    24
    我们曾遇到同样的问题,最终使用 RDLC/ReportViewer 解决。没有本地的 WPF 报告工具(据我所知),而 RDLC 很简单易用且免费。它的运行时开销很小(约为 2Mb),但您必须记得分发它,因为它不是 .NET Framework 的一部分。

    8

    我在Nullskull上找到了一篇文章,它详细解释了如何使用相同的方法逐步创建WPF报表引擎。文章链接为:http://www.nullskull.com/a/1426/wpf-report-engine-part-1.aspx。 - WiiMaxx
    看起来不错,但在2020/11/11项目描述中仍然说“这是一个非常早期的alpha版本,不适用于生产环境。” - UяošKoт

    2
    Scryber如何?它允许使用xml定义PDF报告模板,并在运行时将其绑定到应用程序中的数据。 http://scryber.codeplex.com/

    2

    看一下PdfReports。它是一个基于iTextSharp和EPPlus库构建的代码优先报告引擎。它兼容于.NET 3.5+的Web和Windows应用程序。


    1
    iTextSharp 许可证 :( - juFo
    这个库也有 .NET Core/Full .NET 版本,采用 LGPL 许可证:https://github.com/VahidN/PdfReport.Core - VahidN

    1

    0

    我最近完成了开发自己的报告系统的任务,它基本上由设计环境和数据源管理器组成。第一个任务是开发类似WYSWIG的设计环境。我使用GDI+完成了这个任务,甚至没有考虑打印,因为打印/生成打印预览比我预期的要容易得多,通常只需要将所有内容绘制到打印事件的图形对象上即可。

    我认为在WPF的情况下,它会很相似,所以你唯一需要担心的就是在屏幕上显示你的报告,而打印只需要几行代码即可。


    -1

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接