Контейнеры FlowDocumentScrollViewer, FlowDocumentPageViewer и FlowDocumentReader

129

WPF предоставляет три контейнера, доступных только для чтения, которые можно использовать для отображения потоковых документов:

FlowDocumentScrollViewer

Показывает весь документ с полосой прокрутки, которая позволяет перемещаться по документу, если он превышает размеры контейнера. Этот контейнер не поддерживает разбиение на страницы или несколько колонок (хотя, как и все контейнеры, он поддерживает печать и изменение масштаба).

FlowDocumentPageViewer

Разбивает документ на страницы. Размер каждой страницы совпадает с доступным местом, а пользователь может переходить от одной страницы к другой. Контейнер FlowDocumentPageScrollViewer требует больших затрат, чем FlowDocumentScrollViewer (из-за дополнительных вычислений для разбивки содержимого на страницы).

FlowDocumentReader

Сочетает возможности контейнеров FlowDocumentScrollViewer и FlowDocumentPageViewer. Он позволяет пользователю выбрать, как будет выполняться чтение содержимого — с прокруткой или постранично. Кроме того, в нем есть функция поиска. Контейнер FlowDocumentReader требует больших затрат, чем другие контейнеры потоковых документов.

Для переключения с одного контейнера на другой нужно просто изменить объемлющий дескриптор. Например, ниже показан потоковый документ в контейнере FlowDocumentPageViewer:

<FlowDocumentPageViewer>
        <FlowDocument>
            <Paragraph>Текст</Paragraph>
        </FlowDocument>
</FlowDocumentPageViewer>

Каждый из этих контейнеров имеет дополнительные возможности: изменение масштаба, разбиение на страницы и печать. Они будут описаны ниже.

Изменение масштаба

Все три контейнера документов поддерживают функцию изменения масштаба (zooming), т.е. уменьшения или увеличения размера отображаемого содержимого.

Свойство Zoom контейнера (например, FlowDocumentScrollViewer.Zoom) устанавливает размеры содержимого в виде процентного значения. Обычно начальным значением Zoom является 100, а значения FontSize соответствуют любым другим элементам в окне. Если увеличить значение Zoom до 200, размер текста будет удвоен. Аналогично, если уменьшить его до 50, размер текста будет уменьшен вдвое (можно задавать любое значение из этого диапазона).

Понятно, что процентное значение масштабирования можно задавать вручную. Но можно изменять масштаб и программно, с помощью методов IncreaseZoom() и DecreaseZoom(), которые изменяют значение Zoom на величину, указанную в свойстве ZoomIncrement.

Команды позволяют связать с этими функциями и другие элементы управления. Однако лучше так не делать. Гораздо проще позволить пользователям самостоятельно задавать процент изменения масштаба по своему усмотрению. Контейнер FlowDocumentScrollViewer содержит для этого инструментальную панель с линейкой и ползунком. Чтобы сделать ее видимой, нужно присвоить свойству IsToolbarVisible значение true:

<FlowDocumentScrollViewer MinZoom="50" MaxZoom="1000" 
                            ZoomIncrement="5" Zoom="100" IsToolBarVisible="True">
        ...

На рисунке показан потоковый документ с ползунком для изменения масштаба, расположенным внизу:

Изменение масштаба документа

В контейнерах FlowDocumentPageViewer и FlowDocumentReader ползунок изменения масштаба виден всегда (но можно задать приращение масштаба, а также минимальное и максимальное значения масштабирования).

Изменение масштаба влияет на размер всего, что задано в не зависящих от устройства единицах (а не только на размер шрифта). Например, если в потоковом документе применяются плавающие окошки или рисунки с явно заданной шириной, то эта ширина тоже будет пропорционально изменяться.

Страницы и колонки

Контейнер FlowDocumentPageViewer может разбивать длинный документ на отдельные страницы. Это облегчает чтение длинного содержимого документа. (При использовании прокрутки пользователям постоянно приходится останавливать чтение, прокручивать текст, а затем находить место, с которого нужно продолжить чтение. А если пользователи просматривают последовательность страниц, то они точно знают, где нужно начинать чтение — вверху каждой страницы.)

Количество страниц зависит от размера окна. Например, если разрешить контейнеру FlowDocumentPageViewer занимать все окно, то количество страниц будет изменяться при изменении размеров окна, как показано на рисунке:

Динамическое переразбиение на страницы

Если окно имеет достаточную ширину, контейнер FlowDocumentPageViewer разобьет текст для удобства чтения на несколько колонок:

Автоматическая разбивка на колонки

Имейте в виду, что элементы Floater обычно растягиваются на всю ширину колонки. Их можно сделать уже, задав явным образом ширину, но не шире. А элементы Figure могут легко растянуться на несколько колонок.

Стандартные настройки дают хорошее разбиение на страницы и колонки, но их можно изменять, чтобы получить желаемый результат. Есть две ключевые точки, которые позволяют выполнять расширение: класс FlowDocument, который хранит содержимое, и отдельные элементы Paragraph в документе.

Очевидно, что в некоторых случаях свойства элемента Paragraph, определяющие разрыв колонок, невыполнимы. Например, если абзац слишком большой и не входит на одну страницу, свойство KeepTogether будет проигнорировано, так как абзац все равно придется разбить.

Свойства класса FlowDocument для управления колонками

ColumnWidth

Определяет предпочтительный размер текстовых колонок. Выступает в качестве минимального размера, а FlowDocumentPageViewer подстраивает ширину, чтобы максимально использовать место на странице.

IsColumnWidthFlexible

Определяет, может ли контейнер документа изменять размер колонки. Если равно false, используется точная ширина колонки, определенная в свойстве ColumnWidth. Контейнер FlowDocumentPageViewer не создает частичные колонки, поэтому в результате могут возникнуть пустые места у правого края страницы (или с обеих сторон, если значение FlowDocumentMaxPageWidth меньше ширины окна документа). Если равно true (по умолчанию), то FlowDocumentPageViewer разобьет доступное место на равные колонки с шириной не меньше ColumnWidth.

ColumnGap

Задает промежуток между колонками.

ColumnRuleWidth и ColumnRuleBrush

Позволяет вывести вертикальную линию между колонками. Для нее можно указать ширину и заполнение.

Свойства абзаца для управления колонками

KeepTogether

Определяет, будет ли разбиваться абзац при разрыве страницы. Если равно true, абзац не разбивается и обычно переносится на следующую страницу. (Этот параметр имеет смысл для небольших объемов текста, которые нужно читать как одно целое.)

KeepWithNext

Определяет, можно ли разделить пару абзацев при разрыве страницы. Если равно true, то при разрыве страницы данный абзац не отделяется от следующего. (Этот параметр удобен для заголовков.)

MinOrphanLines

Управляет разбиением абзаца при разрыве страницы. Если этот абзац разбивается при разрыве страницы, то данное значение задает минимальное количество строк, которые должны остаться на первой странице. Если для такого количество строк нет места, абзац будет целиком перенесен на следующую страницу.

MinWindowLines

Управляет разбиением абзаца при разрыве страницы. Если этот абзац разбивается при разрыве страницы, то данное значение задает минимальное количество строк, которые будут перенесены на вторую страницу. Контейнер FlowDocumentPageViewer может перенести строки с первой страницы на вторую, чтобы удовлетворить этому критерию.

Контейнер FlowDocumentPageViewer не является единственным контейнером, поддерживающим разбиение на страницы. FlowDocumentReader позволяет пользователю выбрать между режимом прокрутки (который работает точно так же, как и в FlowDocumentScrollViewer) и двумя страничными режимами. Можно просматривать текст по одной странице (как в контейнере FlowDocumentPageViewer) или по две страницы рядом. Для переключения между режимами просмотра нужно просто щелкнуть на соответствующем значке в правом нижнем углу инструментальной панели FlowDocumentReader.

Загрузка документов из файла

В приведенных до настоящего момента примерах объект FlowDocument объявлялся внутри своего контейнера. Однако после создания превосходного средства для просмотра документов логично применять его и для просмотра другого содержимого (например, чтобы показывать различные темы в окне справки).

Для этого придется динамически загружать содержимое в контейнер с помощью класса XamlReader из пространства имен System.Windows.Markup. К счастью, сделать это нетрудно. Ниже показан весь нужный для этого код (без обработки ошибок, связанных с доступом к файлу):

using System.IO;
using System.Windows.Markup;
   
...   
   
using (FileStream fs = File.Open("...", FileMode.Open))
{
      FlowDocument document = (FlowDocument)XamlReader.Load(fs);

      if (document == null)
             MessageBox.Show("Ошибка при загрузке документа");
      else
             flowdocumentpageviewer_container.Document = document;
}

Так же несложно взять текущее содержимое FlowDocument и сохранить его в XAML-файле с помощью класса XamlWriter. Эта возможность не так полезна — ведь контейнеры, с которыми вы познакомились до настоящего времени, не разрешают пользователям выполнять изменения. Однако она позволяет программно изменять документ в зависимости от действий пользователей или создать объект FlowDocument программным образом и сохранить его прямо на диске.

Вот код, который преобразовывает объект FlowDocument в XAML:

  using (FileStream fs = File.Open("...", FileMode.Create)) 
            { 
                 XamlWriter.Save(flowdocumentpageviewer_container.Document, fs);
            }
Пройди тесты
Лучший чат для C# программистов