Вы не можете использовать один 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()
просто вызывает paginator этого документа для получения соответствующей страницы. В противном случае он отбрасывает этот FlowDocument и создает FlowDocument
для нового раздела, получает его paginator, а затем вызывает GetPage()
для paginator.
Эта стратегия позволяет вам продолжать использовать FlowDocuments
, как и раньше, до тех пор, пока вы можете разбивать данные на «разделы», каждый из которых имеет свой собственный документ. Затем ваш пользовательский пагинатор эффективно обрабатывает все отдельные FlowDocuments как один большой документ. Это похоже на функцию «Мастер-документ» в Word.
Если вы можете визуализировать данные в виде последовательности вертикально расположенных визуальных элементов
В этом случае можно использовать ту же технику. Во время первого прохода все визуальные элементы генерируются по порядку и измеряются, чтобы увидеть, сколько из них уместится на странице. Структура данных построена так, чтобы указывать, какой диапазон визуальных элементов (по индексу или что-либо еще) находится на данной странице. Во время этого процесса каждый раз, когда страница заполняется, следующий визуал помещается на новую страницу. Верхние и нижние колонтитулы будут обрабатываться очевидным образом.
Во время фактической генерации документа реализован метод GetPage()
для восстановления визуалов, которые ранее были размещены на данной странице, и их объединения с использованием вертикальной панели DockPanel или другой панели по вашему выбору.
Я нашел эту технику более гибкой в долгосрочной перспективе, потому что вам не нужно иметь дело с ограничениями FlowDocument
.