您不能使用单个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 的限制。