如何在WPF中生成和打印大型XPS文档?
我想从我的 WPF 应用程序生成(然后打印或保存)大型 XPS 文档(> 400 页)。我们有一些大量的内存数据需要写入 XPS。
如何在不出现 OutOfMemoryException
的情况下完成此操作?有没有办法可以将文档分成块?这通常是如何完成的?我一开始就不应该对大文件使用 XPS 吗?
OutOfMemoryException
的根本原因似乎是巨大的 FlowDocument
的创建。我正在创建完整的 FlowDocument
,然后将其发送给 XPS 文档编写器。这是错误的做法吗?
I would like to generate (and then print or save) big XPS documents (>400 pages) from my WPF application. We have some large amount of in-memory data that needs to be written to XPS.
How can this be done without getting an OutOfMemoryException
? Is there a way I can write the document in chunks? How is this usually done? Should I not be using XPS for large files in the first place?
The root cause of the OutOfMemoryException
seems to be the creation of the huge FlowDocument
. I am creating the full FlowDocument
and then sending it to the XPS document writer. Is this the wrong approach?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
你怎么做?你没有显示任何代码。
我使用 XpsDocumentWriter 分块写入,如下所示:
这对您不起作用吗?
How do you do it? You didn't show any code.
I use an XpsDocumentWriter to write in chunks, like this:
Does this not work for you?
由于对所涉及的特定系统完全无知,我是否可以建议使用阿拉斯加的狼栅栏调试技术来识别问题的根源?我建议这样做是因为其他响应者没有报告您遇到的相同问题。当处理易于重现的错误时,Wolf Fence 非常简单(它在竞争条件等情况下效果不太好)。
现在你可能已经有了可以直接攻击的东西。祝你好运。
Speaking from perfect ignorance of the specific system involved, might I suggest using the Wolf Fence in Alaska debugging technique to identify the source of the problem? I'm suggesting this because other responders are not reporting the same problem you are experiencing. When working with easy-to-reproduce bugs Wolf Fence is dead simple to do (it doesn't work so well with race conditions and the like).
Now you probably have something you can attack directly. Good luck.
您不能使用单个
FlowDocument
来生成大型文档,因为您会耗尽内存。但是,如果可以将输出生成为FlowDocument
序列或极高的ItemsControl
序列,那么这是可能的。我发现最简单的方法是子类化
DocumentPaginator
并将子类的实例传递给XpsDocumentWriter.Write
:WidgetPaginator
类其本身非常简单:当然,您仍然需要编写实际创建页面的代码。
如果您想使用一系列 FlowDocuments 来创建文档
如果您使用一系列
FlowDocuments
来一次布置文档的一部分而不是全部一旦,您的自定义分页器可以分两遍工作:FlowDocument
,然后获取一个DocumentPaginator
来检索页数。每个部分的FlowDocument
在页面计数后将被丢弃。GetPage()
的数字位于最近创建的FlowDocument
中,则GetPage()
只需调用该文档的分页器来获取适当的页面。否则,它会丢弃该 FlowDocument 并为新部分创建一个FlowDocument
,获取其分页器,然后在该分页器上调用GetPage()
。此策略允许您继续像以前一样使用
FlowDocuments
,只要您可以将数据分成“部分”,每个部分都有自己的文档。然后,您的自定义分页器可以有效地将所有单独的 FlowDocuments 视为一个大文档。这类似于Word的“主文档”功能。如果您可以将数据呈现为一系列垂直堆叠的视觉效果
在这种情况下,可以使用相同的技术。在第一遍期间,所有视觉效果都会按顺序生成并进行测量,以了解页面上适合多少个视觉效果。构建数据结构来指示在给定页面上找到的视觉效果范围(通过索引或其他方式)。在此过程中,每次页面填满时,下一个视觉效果都会放置在新页面上。页眉和页脚将以显而易见的方式处理。
在实际文档生成过程中,实现 GetPage() 方法来重新生成先前决定位于给定页面上的视觉效果,并使用垂直 DockPanel 或您选择的其他面板将它们组合起来。
我发现从长远来看,这种技术更加灵活,因为您不必处理
FlowDocument
的限制。You cannot use a single
FlowDocument
for generating large documents because you will run out of memory. However if it is possible to generate your output as a sequence ofFlowDocument
or as an extremely tallItemsControl
, it is possible.I've found the easiest way to do this is to subclass
DocumentPaginator
and pass an instance of my subclass toXpsDocumentWriter.Write
:The
WidgetPaginator
class itself is quite simple:Of course you still have to write the code that actually creates the pages.
If you want to use a series of FlowDocuments to create your document
If you're using a sequence of
FlowDocuments
to lay out your document one section at a time instead of all at once, your custom paginator can work in two passes:FlowDocument
for each section, then gets aDocumentPaginator
to retrieve the number of pages. Each section'sFlowDocument
is discarded after the pages are counted.GetPage()
is in the most recentFlowDocument
created,GetPage()
simply calls that document's paginator to get the appropriate page. Otherwise it discards that FlowDocument and creates aFlowDocument
for the new section, gets its paginator, then callsGetPage()
on the paginator.This strategy allows you to continue to use
FlowDocuments
as you have been, as long as you can break the data into "sections" each with its own document. Your custom paginator then effectively treats all the individual FlowDocuments as one big document. This is similar to Word's "Master Document" feature.If you can render your data as a sequence of vertically-stacked visuals
In this case, the same technique can be used. During the first pass, all visuals are generated in order and measured to see how many will fit on a page. A data structure is built to indicate which range of visuals (by index or whatever) are found on a given page. During this process each time a page fills up, the next visual is placed on a new page. Headers and footers would be handled in the obvious way.
During the actual document generation, the
GetPage()
method is implemented to regenerate the visuals previously decided to be on a given page and combine them using a vertical DockPanel or other panel of your choice.I've found this technique more flexible in the long run because you don't have to deal with the limitations of
FlowDocument
.我可以确认 XPS 不会在长文档上引发内存不足。无论是在理论上(因为 XPS 上的操作是基于页面的,它不会尝试将整个文档加载到内存中),还是在实践中(我使用基于 XPS 的报告,并且看到失控的错误消息总计达数千条)页)。
难道问题出在一个特别大的页面上?例如,一张巨大的图像?大页面具有高 DPI 分辨率?如果文档中的单个对象太大而无法一次分配,则会导致内存不足异常。
I can confirm that XPS does not throw out-of-memory on long documents. Both in theory (because operations on XPS are page-based, it doesn't try to load whole document in memory), and in practice (I use XPS-based reporting, and seen run-away error messages add up to many thousands of pages).
Could it be that the problem is in a single particularly large page? A huge image, for example? Large page with high DPI resolution? If single object in document is too big to be allocated at once, it will lead to out-of-memory exception.
您是否使用过 sos 来找出是什么耗尽了所有内存?
可能是在文档生成过程中创建了托管或非托管对象,并且在文档完成(或根本不完成)之前它们不会被释放。
Rico Mariani 的跟踪托管内存泄漏可能是的帮助。
Have you used sos to find out what is using up all the memory?
It could be that managed or unmanaged objects are being created during the production of your document, and they're not being released until the document is finished (or not at all).
Tracking down managed memory leaks by Rico Mariani could be of help.
就像你说的:可能内存中的固定文档消耗了太多的内存。
也许您单独编写 XPS 页面(并确保每次都发布固定文档),然后使用合并的方法可能会取得成果。
你能把每一页分开写吗?
缺口。
附:请随时直接与我联系([电子邮件受保护]);我们在 NiXPS 做了很多 XPS 工作,我非常有兴趣帮助您解决这个问题。
like you say: probably the in-memory FixedDocument is consuming too much memory.
Maybe an approach in which you write out the XPS pages each individually (and make sure the FixedDocument gets released each time), and then use a merger afterwards could prove fruitful.
Are you able to write each page separately?
Nick.
ps. Feel free to concact me directly ([email protected]); we do a lot of XPS stuff at NiXPS, and I'm very interested in helping you getting this issue resolved.