我想从我的 WPF 应用程序生成(然后打印或保存)大型 XPS 文档(>400 页)。我们有大量的内存数据需要写入 XPS。
没有得到一个怎么能做到这一点OutOfMemoryException
?有没有办法可以分块编写文档?这通常是怎么做的?我不应该首先将 XPS 用于大文件吗?
其根本原因OutOfMemoryException
似乎是巨大的创造FlowDocument
。我正在创建完整FlowDocument
内容,然后将其发送给 XPS 文档编写器。这是错误的方法吗?
你怎么做呢?你没有显示任何代码。
我使用 XpsDocumentWriter 以块的形式写入,如下所示:
FlowDocument flowDocument = . .. ..;
// write the XPS document
using (XpsDocument doc = new XpsDocument(fileName, FileAccess.ReadWrite))
{
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocument).DocumentPaginator;
// Change the PageSize and PagePadding for the document
// to match the CanvasSize for the printer device.
paginator.PageSize = new Size(816, 1056);
copy.PagePadding = new Thickness(72);
copy.ColumnWidth = double.PositiveInfinity;
writer.Write(paginator);
}
这对你不起作用吗?
由于完全不了解所涉及的特定系统,我是否建议使用阿拉斯加的狼栅栏调试技术来确定问题的根源?我建议这样做是因为其他响应者没有报告您遇到的相同问题。当处理易于重现的错误时,Wolf Fence 非常简单(它在竞争条件等情况下效果不佳)。
现在你可能有了可以直接攻击的东西。祝你好运。
您不能使用单个FlowDocument
来生成大型文档,因为您将耗尽内存。但是,如果可以将输出生成为一个序列FlowDocument
或一个非常高的序列ItemsControl
,那么它是可能的。
我发现最简单的方法是子类DocumentPaginator
化并将我的子类的实例传递给XpsDocumentWriter.Write
:
var document = new XpsDocument(...);
var writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
writer.Write(new WidgetPaginator { Widget = widgetBeingPrinted, PageSize = ... });
类WidgetPaginator
本身非常简单:
class WidgetPaginator : DocumentPaginator, IDocumentPaginatorSource
{
Size _pageSize;
public Widget Widget { get; set; }
public override Size PageSize { get { return _pageSize; } set { _pageSize = value; } }
public override bool IsPageCountValid { return true; }
public override IDocumentPaginatorSource Source { return this; }
public override DocumentPaginator DocumentPaginator { return this; }
public override int PageCount
{
get
{
return ...; // Compute page count
}
}
public override DocumentPage GetPaget(int pageNumber)
{
var visual = ...; // Compute page visual
Rect box = new Rect(0,0,_pageSize.With, _pageSize.Height);
return new DocumentPage(visual, _pageSize, box, box);
}
当然,您仍然必须编写实际创建页面的代码。
如果您想使用一系列 FlowDocuments 来创建您的文档
如果您使用一系列FlowDocuments
来一次布置一个部分而不是一次全部布置文档,则您的自定义分页器可以分两遍工作:
FlowDocument
它为每个部分创建一个,然后获取一个DocumentPaginator
以检索页数。在计算页数后,每个部分FlowDocument
都将被丢弃。GetPage()
是最近FlowDocument
创建的,则GetPage()
只需调用该文档的分页器以获取适当的页面。否则,它会丢弃该 FlowDocument 并FlowDocument
为新部分创建一个,获取其分页器,然后调用GetPage()
分页器。FlowDocuments
只要您可以将数据分成“部分”,每个部分都有自己的文档,这种策略就可以让您继续使用。然后,您的自定义分页器有效地将所有单个 FlowDocuments 视为一个大文档。这类似于 Word 的“主文档”功能。
如果您可以将数据呈现为一系列垂直堆叠的视觉效果
在这种情况下,可以使用相同的技术。在第一遍中,所有视觉效果都按顺序生成并测量以查看页面上适合多少视觉效果。构建数据结构以指示在给定页面上找到的视觉范围(按索引或其他)。在此过程中,每次页面填满时,都会将下一个视觉对象放置在新页面上。页眉和页脚将以显而易见的方式处理。
在实际文档生成过程中,该GetPage()
方法被实施以重新生成先前决定在给定页面上的视觉效果,并使用垂直 DockPanel 或您选择的其他面板将它们组合起来。
从长远来看,我发现这种技术更加灵活,因为您不必处理FlowDocument
.
我可以确认 XPS 不会对长文档造成内存不足。理论上(因为 XPS 上的操作是基于页面的,它不会尝试将整个文档加载到内存中)和实践中(我使用基于 XPS 的报告,并且看到的失控错误消息加起来多达数千条)页)。
难道问题出在一个特别大的页面上?例如,一个巨大的图像?具有高 DPI 分辨率的大页面?如果文档中的单个对象太大而无法一次分配,则会导致内存不足异常。
你有没有用 sos 找出是什么用完了所有的内存?
可能是在文档制作过程中创建了托管或非托管对象,并且在文档完成(或根本不发布)之前不会发布它们。
追踪Rico Mariani 的托管内存泄漏可能会有所帮助。
就像你说的:可能内存中的 FixedDocument 消耗了太多内存。
也许您单独写出每个 XPS 页面(并确保每次都发布 FixedDocument)然后使用合并的方法可能会证明是富有成效的。
你能单独写每一页吗?
缺口。
附言。请随时直接与我联系(info@nixps.com);我们在 NiXPS 做了很多 XPS 工作,我很想帮助您解决这个问题。