我想从我的WPF应用程序生成(然后打印或保存)大型XPS文档(> 400页)。我们有一些需要写入XPS的大量内存数据。
如果没有获得OutOfMemoryException
怎么办呢?有没有办法可以把文件写成块?这通常是怎么做的?我不应该首先将XPS用于大文件吗?
OutOfMemoryException
的根本原因似乎是创建巨大的FlowDocument
。我正在创建完整的FlowDocument
,然后将其发送到XPS文档编写器。这是错误的做法吗?
答案 0 :(得分:5)
你是怎么做到的?您没有显示任何代码。
我使用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);
}
这不适合你吗?
答案 1 :(得分:4)
完全不了解所涉及的具体系统,我是否可以建议在阿拉斯加调试技术中使用Wolf Fence来确定问题的根源?我建议这样做是因为其他响应者没有报告您遇到的同样问题。使用易于重现的bug时,Wolf Fence很容易做到(它在竞争条件等方面效果不佳)。
现在你可能有一些你可以直接攻击的东西。祝你好运。
答案 2 :(得分:4)
您不能使用单个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
的限制。
答案 3 :(得分:3)
我可以确认XPS在长文档中不会丢失内存。两者在理论上(因为XPS上的操作是基于页面的,它不会尝试将整个文档加载到内存中),并且在实践中(我使用基于XPS的报告,并且看到失控的错误消息总计数千个页)。
问题是在一个特别大的页面中吗?例如,巨大的形象?具有高DPI分辨率的大页面?如果文档中的单个对象太大而无法立即分配,则会导致内存不足异常。
答案 4 :(得分:0)
您是否使用过sos来查找耗尽所有内存的内容?
可能是在文档制作过程中创建托管或非托管对象,并且在文档完成(或根本没有)之前它们不会被释放。
Rico Mariani的答案 5 :(得分:0)
就像你说的那样:内存中的FixedDocument可能消耗了太多内存。
也许是一种单独写出XPS页面的方法(并确保每次都释放FixedDocument),然后使用合并可以证明是富有成效的。
您是否可以单独编写每个页面?
尼克。
PS。随意直接与我联系(info@nixps.com);我们在NiXPS上做了很多XPS工作,我非常有兴趣帮助你解决这个问题。