我有一个即将进行的项目,该项目必须能够根据其数据打印简单的报告。它将基于 WPF,我想知道该走哪条路。
我知道WPF引入了自己的打印技术(基于XPS),看起来很容易使用。然而,我心里想知道使用 ReportViewer 控件并将其嵌入到 Windows 窗体主机控件中是否会更容易,因为这将使用户能够导出为各种格式以及打印。
有人有过从 WPF 打印/报告的经验吗?您会推荐哪个方向?
RDL 的局限性
我最初使用 RDLC/ReportViewer 来使用 WPF 进行打印,但发现它非常有限。我发现的一些限制是:
- RDL 只能创建最无聊的报告
- 使用 RDL 创建报告比直接使用 WPF 需要做更多的工作:与 Expression Blend 相比,设计工具非常原始,而且 RDL 仅在表格中处理
- 我无法使用 ControlTemplates、DataTemplates、Styles 等
- 我的报告字段和列无法根据数据大小有效地调整大小和重新排列
- 图形必须作为图像导入 - 它不能作为矢量绘制或编辑
- 项目的定位需要代码隐藏而不是数据绑定
- 缺乏变换
- 非常原始的数据绑定
直接从 WPF 打印非常简单
由于这些限制,我研究了使用纯 WPF 创建报告,发现它确实非常简单。 WPF 允许您实现自己的DocumentPaginator
可以生成页面的子类。
我开发了一个简单的 DocumentPaginator 子类,它采用任何视觉对象、分析视觉对象树并隐藏选定的元素来创建每个页面。
文档分页器详细信息
以下是我的 DocumentPaginator 子类在初始化期间执行的操作(在获取第一个 PageCount 时或在第一次 GetPage() 调用期间调用):
- 扫描视觉树并制作 ItemsControls 内所有滚动面板的地图
- 从最外层开始,使 ItemsControls 中的项目从最后到第一个不可见,直到视觉对象适合单个页面而无需滚动。如果最外面的面板不能减少得足够多,则减少内部面板,直到成功或每一层只有一个项目。将可见项目集记录为第一页。
- 隐藏已在第一页上显示的最低级别的项目,然后使后续项目可见,直到它们不再适合页面。将除最后添加的项目之外的所有项目记录为第二页。
- 对所有页面重复该过程,将结果存储在数据结构中。
我的DocumentPaginator的GetPage方法如下:
- 在初始化期间生成的数据结构中查找给定的页码
- 按照数据结构中的指示隐藏和显示可视化树中的项目
- 设置 PageNumber 和 NumberOfPages 附加属性,以便报表可以显示页码
- 冲洗调度程序(
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => {} ));
) 来完成任何后台渲染任务
- 创建一个与页面大小相同的矩形,其 VisualBrush 是要打印的视觉效果
- 测量、排列和更新布局矩形,然后返回它
事实证明,这是非常简单的代码,并且允许我将几乎可以使用 WPF 创建的任何内容转换为页面并打印它。
额外的报告支持
现在我的分页器正在工作,我不再需要非常担心我是为屏幕还是纸张创建 WPF 内容。事实上,我为数据输入和编辑构建的 UI 通常也非常适合打印。
从那里我添加了一个简单的工具栏和一些隐藏代码,从而形成了一个围绕 WPF 构建的成熟的报告系统,该系统的功能远比 RDL 强大。我的报告代码可以导出到文件、打印到打印机、剪切/粘贴页面图像以及剪切/粘贴 Excel 数据。我还可以通过单击复选框将任何 UI 切换到“打印视图”,以查看打印后的外观。所有这一切都只需几百行 C# 和 XAML!
此时,我认为 RDL 具有但我的报告代码不具备的唯一功能是生成格式化的 Excel 电子表格的能力。我知道如何做到这一点,但到目前为止还没有必要 - 仅剪切和粘贴数据就足够了。
根据我的经验,我的建议是编写一个分页器,然后开始使用 WPF 本身来创建报告。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)