Статьи:







Рекламка:

Предупреждение

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

Гибкое отображение информации с помощью документов нефиксированного формата



Гибкое отображение информации с помощью документов нефиксированного формата




Автор: Маркус Эггер
Источник: здесь



Windows Presentation Foundation (WPF) предоставляет великолепный набор возможностей. На самом деле их так много, что некоторые, даже очень значимые, не получают и доли того внимания, которого они заслуживают. Превосходным примером являются документы нефиксированного формата (Flow Documents), позволяющие разработчикам создавать документы прямо в WPF. В своей статье «XPS Documents: A First Look at APIs For Creating XML Paper Specification Documents» в «MSDN Magazine» за январь 2006 г. Боб Уотсон (Bob Watson) подробно рассмотрел документы XPS в WPF, но документы нефиксированного формата другие. Спецификация XPS (XML Paper Specification) ориентирована на вывод на печать и постраничную информацию, тогда как документы нефиксированного формата предназначены для чтения с экрана и предоставляют более динамичную и, возможно, более сложную модель. Документы нефиксированного формата подходят практически для любой текстовой информации, от описания продукции до целых книг.

Отображение текста бесспорно является одной из наиболее важных характеристик UI. В интерфейсах WPF вы зачастую используете для отображения текста такие элементы управления, как Label. Однако во многих случаях вам необходимо нечто большее простого вывода нескольких слов. Документы нефиксированного формата предоставляют более сложный подход, хотя в основе своей они очень просты. Они определяют расположение текста способом, напоминающим HTML-документы, но обладают большими возможностями и предлагают значительно больше вариантов структуры.

Обычно документы нефиксированного формата определяются с применением XAML, стандартного языка разметки, основанного на XML. XAML интуитивно понятен применительно к документам нефиксированного формата, в основном за счет схожести с HTML. В следующем примере создается абзац текста, где обычное полужирное начертание применено к нескольким словам:
<FlowDocument
  xmlns='http://schemas.microsoft.com/winfx/2006/
    xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  <Paragraph>The quick <Bold>brown fox</Bold> jumps over
    the lazy dog.
</Paragraph>
</FlowDocument>


Как видите, схожесть с HTML даже более очевидна, чем в других XAML UI. Названия элементов другие, но, по крайней мере для простых документов, концепция очень похожа. Обычно документы нефиксированного формата начинаются с корневого элемента FlowDocument, содержащего несколько блоков. Блоки являются элементами потока, обычно абзацами текста, как в приведенном примере (хотя существуют и другие типы блоков). В свою очередь абзацы могут содержать другие элементы, такие как два выделенных полужирным слова в этом примере. Заметьте: как и в любом другом XAML-документе, у корневого элемента должны быть специфические для XAML определения пространства имен, чтобы его распознать. Это деталь реализации, относящаяся исключительно к XAML и не имеющая отношения к документам нефиксированного формата. Задание пространства имен необходимо только для автономных (standalone) документов нефиксированного формата. (Они могут быть частью большего XAML UI, и в таком случае определения пространства имен достаются корневому элементу UI.)

Конечно, пользователи никогда не видят XAML для документов нефиксированного формата (в отличие от исходного текста HTML, который можно просмотреть в браузере), как и XAML для любого другого UI-элемента. Для этого конкретного примера результат можно увидеть несколькими способами. Пожалуй, проще всего будет набрать пример в XamlPad - утилите, поставляемой с Windows SDK (рис. 1).


Рис. 1. Очень простой документ нефиксированного формата, отображаемый в XamlPad



Конечно же, это очень простой пример; определение документа и внутренней структуры может быть куда более сложным. Документы нефиксированного формата поддерживают все параметры форматирования, такие как курсив, подчеркнутый шрифт, цвета, гарнитуры и многое другое. В лист. 1 показан чуть более сложный пример, а результат представлен на рис. 2.

Листинг 1. Дополнительное форматирование и маркированный список
<FlowDocument
  xmlns='http://schemas.microsoft.com/winfx/2006/
    xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  <Paragraph FontFamily="Calibri" FontWeight="Bold"
    FontSize="24">
    WPF Flow Documents</Paragraph>
  <Paragraph>WPF Flow Documents are <Italic>
    quite sophisticated</Italic>. They support all common
    layout options, as well as many you probably
    do <Span Foreground="Red">not expect</Span>.</Paragraph>
  <List>
    <ListItem><Paragraph>First List Item</Paragraph></ListItem>
    <ListItem><Paragraph>Second List Item</Paragraph>
    </ListItem>
    <ListItem><Paragraph>Third List Item</Paragraph></ListItem>
  </List>
  <Paragraph>Of course, Flow Documents also support
    the definition of <Bold><Span FontFamily="Comic Sans MS"
    FontSize="24" Foreground="Blue">in line</Span></Bold> font
    sizes and font faces.
  </Paragraph>
</FlowDocument>



Рис. 2. Документ нефиксированного формата с чуть более сложным форматированием



В этом примере показано несколько абзацев со встроенным форматированием. Он также демонстрирует другой тип блока, List, который, что неудивительно, содержит список из нескольких элементов. Обратите внимание, что каждый из элементов списка в свою очередь является контейнером для других блоков. Поэтому, вместо того чтобы поместить текст внутрь элемента списка, я добавил в каждый их них по абзацу. Я мог бы добавить по несколько абзацев или блоков любого другого типа в каждый элемент. Это позволяет создавать сложные структуры внутри одного элемента списка, что обычно является проблемой в форматах наподобие HTML, позволяющих размещать лишь простые строки текста в каждом элементе списка.

Основы документов нефиксированного формата


Теперь, когда вы увидели несколько базовых документов нефиксированного формата, вернемся назад и снова рассмотрим некоторые основные понятия. Как вы уже заметили, такие документы являются наборами блоков. На внутреннем уровне все блоки - WPF-классы, производные от System.Windows.Documents.Block. В свою очередь класс Block наследует (несколько шагов далее по цепочке) от класса ContentElement, достаточно низкоуровневого WPF-класса, специально оптимизированного для определения документов. Это чем-то напоминает применяемые для создания WPF-интерфейсов элементы управления, которые все наследуют от класса UIElement. Оба дерева наследования схожи по концепции, но не абсолютно одинаковы. То есть элементы управления WPF и блоки нельзя комбинировать напрямую. Например, название кнопки не может быть абзацем текста, а абзац не может содержать кнопку. Есть тонкие различия между элементами управления и блоками, вызванные тем, что разметка внутри элемента управления и внутри блока работает весьма разным образом. К счастью, разрыв между этими двумя типами WPF-элементов, который надо преодолеть, не слишком велик. Кнопки, например, могут содержать объекты TextBlock, состоящие из форматированного текста, а блоки могут включать любой элемент управления WPF при помощи специального класса блока BlockUIContainer. Это означает, что документы нефиксированного формата могут содержать WPF-элементы любого типа (включая интерактивные UI, медийные элементы и элементы, относящиеся к трехмерной графике), а с другой стороны, такие документы могут быть частью любого WPF UI в качестве либо дополнительного элемента разметки для контента элемента управления, либо отдельного документа, в частности описания товара в приложении, применяемом для обработки коммерческих операций.

Теоретически список возможных блоков бесконечен, так как разработчики могут создавать свои классы блоков путем наследования и таким образом вносить собственные расширения в механизм рендеринга документов. Это предоставляет свободу, недоступную ни в каком другом известном мне механизме рендеринга документов. Тем не менее обычно авторы документов используют ограниченный набор типов блоков. Перечень наиболее важных типов блоков приведен в табл. 1.

Табл. 1. Важные типы блоков
  • Paragraph - Содержит текст, возможно форматированный
  • List - Содержит списки разного типа (нумерованный, маркированный и т. д.)
  • Table - Содержит таблицы, похожие на таблицы Microsoft Word или HTML
  • BlockUIContainer - Содержит различные UI-элементы, становящиеся частью общей разметки
  • Section - Содержит группу прочих блоков. Разделы удобны для применения общих атрибутов к группам блоков, например одних и тех же атрибутов шрифта к нескольким абзацам



При создании документов нефиксированного формата в XAML вы просто создаете экземпляры объектов определенных типов. Рассмотрим следующий фрагмент XAML (я буду опускать определение пространства имен, чтобы не усложнять примеры):
<FlowDocument>
  <Paragraph>Hello World!</Paragraph>
</FlowDocument>


Здесь создается экземпляр класса FlowDocument и экземпляр класса Paragraph (с текстом «Hello World!»). Этот абзац добавляется в набор блоков класса FlowDocument. Заметьте: как всегда в XAML, имена элементов чувствительны к регистру букв и точно совпадают с именами классов, доступных в WPF. Вы могли бы создать тот же документ программно:
FlowDocument doc = new FlowDocument();
Paragraph para = new Paragraph();
para.Inlines.Add("Hello World!");
doc.Blocks.Add(para);


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

Во многих случаях сами абзацы содержат сложно отформатированную информацию, что также достигается за счет создания объектов:
<Paragraph>Hello <Bold>World!</Bold></Paragraph>


Здесь абзац содержит два сегмента текста - «Hello» (с форматированием по умолчанию) и «World!» (с полужирным начертанием). Это интересно тем, что XAML не просто создает экземпляр объекта абзаца и задает его текст как элементарную строку, а конструирует абзац с двумя дочерними сегментами, каждый из которых содержит текст с разным форматированием. В WPF такие сегменты называются подставляемыми в строку элементами (inlines). Точно так же как документ нефиксированного формата может содержать несколько блоков разного типа, абзацы могут содержать подставляемые в строку элементы разных типов. Некоторые такие элементы, называемые Span, представляют собой сегмент текста с определенными параметрами форматирования. Элемент Bold, использованный в этом примере, - особый случай Span, где начертание шрифта по умолчанию установлено полужирным. Другим типом подставляемого в строку элемента является Run, сегмент текста с форматированием по умолчанию. Поэтому приведенный выше XAML-код - просто сокращение следующего кода:
<Paragraph>
  <Run>Hello </Run>
  <Bold>World!</Bold>
</Paragraph>


Конечно, гораздо удобнее обходиться без определения каждого подставляемого в строку элемента в XAML, но, если вам придется создавать тот же пример программно, важно понимать концепцию подобных элементов, поскольку их нельзя опускать в коде. Вот программный эквивалент двух предыдущих примеров:
Paragraph para = new Paragraph();
para.Inlines.Add(new Run("Hello "));
Bold b = new Bold();
b.Inlines.Add("World!");
para.Inlines.Add(b);


Bold - специальная версия Span, где начертание шрифта по умолчанию установлено полужирным; тип Bold наследует от класса Span и переопределяет свойство FontWeight. Есть похожие специальные элементы Span, такие как Italic и Underline. Однако эти элементы не обязательны, поскольку можно использовать Span по умолчанию и установить подходящие свойства:
<Paragraph>Hello <Span FontWeight="Bold">World!</Span></Paragraph>


Конечно, возможность напрямую устанавливать атрибуты наподобие полужирного начертания и курсива, помещая определенную часть текста в теги Bold и Italic, очень удобна и интуитивно понятна, поэтому значительно чаще используется <Bold> вместо <Span FontWeight="Bold">. Тем не менее элемент <Span> очень полезен, так как помимо простого полужирного начертания существует значительное число свойств, которые можно установить, и у большинства из них нет индивидуального типа Span. Типичный пример - гарнитуры шрифтов. В отличие от HTML в документах нефиксированного формата нет элементов <Font>. Вместо этого управление шрифтами осуществляется следующим образом:
<Paragraph>Hello <Span FontFamily="Comic Sans MS"
    FontSize="24">
    World!</Span></Paragraph>


Многие из свойств, такие как FontFamily, наличествуют во всех без исключения классах для документов нефиксированного формата. Например, если бы вам захотелось установить шрифт для целого абзаца, а не для подставляемого в строку элемента, можно было бы обойтись без Span:
<Paragraph FontFamily="Comic Sans MS"
    FontSize="24">Hello World!</Paragraph>


Есть и другие подставляемые в строку элементы, помимо Span и Run. Вот некоторые из других наиболее интересных подставляемых в строку элементов.

Figure - Эти элементы слегка необычны, потому что содержат блоки. В каком-то смысле элементы Figure выглядят почти как мини-документы с нефиксированным форматом внутри документов с нефиксированным форматом. Элементы Figure часто используются для расширенной разметки, например для включения изображений внутрь абзацев, когда обычный текст «обтекает» изображение.

Floater - Это облегченный вариант элемента Figure. Он не поддерживает всех возможностей позиционирования элементов Figure, но, если вам нужно лишь добиться выравнивания, отличающегося от стандартного выравнивания абзаца, элементы Floater могут пригодиться.

LineBreak - Делают именно то, что предполагает их название: дают возможность вставлять разрывы строк внутри абзаца.

InlineUIContainer - Это подставляемый в строку эквивалент BlockUIContainer. Если вы хотите скомбинировать элемент управления WPF любого типа с подставляемыми в строку элементами (например, поместить кнопку внутри текста абзаца), InlineUIContainer - то, что надо.

Элементы Figure постоянно используются в документах нефиксированного формата (элементы LineBreak тоже, но они вряд ли заслуживают подробного описания). В следующем примере Figure применяется для вывода изображения в составе большей композиции:
<Paragraph>
  <Figure Width="200">
    <BlockUIContainer>
      <Image Source="Pictures\Humpback Whale.jpg" />
    </BlockUIContainer>
    <Paragraph Foreground="Blue" FontFamily="Consolas">
        The Whale</Paragraph>
  </Figure>
  The quick brown fox jumps over the lazy dog.
   The quick brown...
</Paragraph>


Обратите внимание, что в документах нефиксированного формата WPF нет блока Image. Вместо этого изображения встраиваются в виде BlockUIContainer при помощи стандартных для WPF элементов управления Image. (Такой же подход используется для контента наподобие видео или интерактивных трехмерных моделей внутри документа нефиксированного формата.) На рис. 3 показан документ, похожий на приведенный в предыдущем примере.


Рис. 3. Текст «обтекает» изображение и заголовок



Просмотр документов нефиксированного формата


Теперь вы знаете, как создавать простые документы нефиксированного формата и просматривать их в XamlPad. До этого момента я обходил вопрос реального просмотра документов нефиксированного формата. В конце концов, вы же не думаете, что ваши пользователи станут запускать XamlPad и вставлять туда XAML-код документа. Один из вариантов просмотра документа нефиксированного формата - сохранить его в виде файла с расширением XAML и открыть его двойным щелчком в Windows Explorer. Запустится приложение по умолчанию, сопоставленное с XAML-файлами (обычно Internet Explorer), которое отобразит документ. Результат показан на рис. 4.


Рис. 4. Документ нефиксированного формата XAML в Internet Explorer



Тот факт, что Internet Explorer (и другие браузеры) может отображать XAML-контент, представляет особый интерес, потому что это позволяет вам выводить документы нефиксированного формата как часть своих веб-приложений. Другими словами, если вы загрузите документы нефиксированного формата XAML на веб-сервер и кто-то будет просматривать эти файлы, он увидит нечто похожее на рис. 4 (на компьютере пользователя должен быть установлен Microsoft .NET Framework 3.0). Конечно же, все это работает и в динамическом режиме. Если ваше веб-приложение ASP.NET (или любая другая серверная технология) генерирует документ нефиксированного формата XAML «на лету» и возвращает его в качестве результата (и если тип контента правильно задан как «application/xaml+xml»), то документ нефиксированного формата появляется как часть этого веб-приложения, что во многих ситуациях весьма полезно. В лист. 2 показана простая страница ASP.NET, создающая документ нефиксированного формата.

Листинг 2. Динамический документ нефиксированного формата ASP.NET
<%@ Page Language="C#" ContentType="application/xaml+xml" %>
<FlowDocument
  xmlns='http://schemas.microsoft.com/winfx/2006/
    xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>

  <Paragraph FontFamily="Calibri" FontWeight="Bold"
    FontSize="24">
    WPF Flow Documents
  </Paragraph>

  <Paragraph FontFamily="Calibri" FontWeight="Bold"
      FontSize="12">
    <%= DateTime.Now.ToString("d") %>
  </Paragraph>

  <Paragraph>
    The quick brown fox jumps over the lazy dog. ...
  </Paragraph>
</FlowDocument>


Отображение документов нефиксированного формата


Вероятно вы заметили, что при отображении документа нефиксированного формата (и в браузере, и в XamlPad) появляется не только он. В частности, вдоль нижней части документа выводятся элементы управления. Как видно на рис. 5, по умолчанию документы нефиксированного формата отображаются при помощи элемента управления FlowDocumentReader, предоставляющего набор стандартных возможностей, таких как масштабирование, разбиение по страницам, различные режимы просмотра и даже поиск. Оказывается, документы нефиксированного формата нужно размещать в каком-либо элементе управления, способном их отображать. По умолчанию средством просмотра таких документов является элемент управления FlowDocumentReader, экземпляр которого создается автоматически, если только вы явным образом не используете другой элемент управления. На данный момент WPF предоставляет три элемента управления для просмотра документов нефиксированного формата.


Рис. 5. Управляющие кнопки в FlowDocumentReader



FlowDocumentScrollViewer - Этот элемент управления отображает документы непрерывным потоком с полосой прокрутки по аналогии с веб-страницами или режимом просмотра Web Layout в Microsoft Word. На рис. 6 показан документ в FlowDocumentScrollViewer.


Рис. 6. Применение FlowDocumentScrollViewer



FlowDocumentPageViewer - Выводит документы нефиксированного формата в виде отдельных страниц с пролистыванием страниц вместо прокрутки. Это напоминает режим полноэкранного просмотра в Word. FlowDocumentPageViewer показан на рис. 7. Здесь документ с рис. 6 отображается в элементе управления FlowDocumentPageViewer и полоса прокрутки замещена механизмом пролистывания.


Рис. 7. Применение FlowDocumentPageViewer



FlowDocumentReader - Сочетает в себе оба предыдущих и позволяет переключаться между двумя способами. Является элементом управления по умолчанию для документов нефиксированного формата и зачастую оказывается замечательным выбором для приложений, отображающих сложные тексты. На рис. 8 документ, показанный на рис. 6 и 7, отображается в элементе управления FlowDocumentReader, сочетающем постраничный просмотр и прокрутку. Кроме того, он поддерживает поиск, который в других элементах управления по умолчанию скрыт (в них для поиска нужно выполнить команду ApplicationCommands.Find или нажать сочетание клавиш Ctrl+F). Он также поддерживает режим многостраничного просмотра, слегка изменяя постраничное отображение и способ вывода колонок и иллюстраций.


Рис. 8. Применение FlowDocumentReader



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

Как определить требуемый элемент управления? Простой, хоть и довольно грубый, подход - добавить нужный элемент управления в XAML-код документа:
<FlowDocumentScrollViewer
  xmlns='http://schemas.microsoft.com/winfx/2006/
     xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  <FlowDocument>
    <Paragraph>The quick <Bold>brown fox</Bold> jumps over the
        lazy dog.</Paragraph>
  </FlowDocument>
</FlowDocumentScrollViewer>


В этом примере корень документа сделан тегом FlowDocumentScrollViewer. Это означает, что вы определяете не отдельный документ, а полный XAML-интерфейс, использующий FlowDocumentScrollViewer в качестве корня. Содержимым FlowDocumentScrollViewer является документ нефиксированного формата из самого первого примера. (Заметьте, что определения пространства имен теперь относятся к тегу FlowDocumentScrollViewer, а не к тегу документа.) Иллюстрации на рис. 6, 7 и 8 были созданы с применением этого подхода и с использованием различных элементов управления для просмотра в качестве корневого элемента.

Почему я считаю этот подход грубым? Потому что с точки зрения архитектуры у него есть проблемы, вызванные смешением определения UI с реальными данными. Гораздо желательнее держать документ отдельно от его интерфейса. Смешение средства просмотра с документом - все равно что создать таблицу SQL Server и каким-то образом указать, что она может быть отображена только в элементе DataGrid из Windows Forms. Есть несколько способов хранить документ отдельно от определения UI. Если вы хотите отображать документы нефиксированного формата как часть веб-приложения, используя уже продемонстрированный для ASP.NET подход, то можете создать страницу ASP.NET с нужным элементом управления для просмотра и заполнить ее отделенным контентом (хранящимся, возможно, в базе данных), применяя стандартный для ASP.NET код.

С другой стороны, в типичном WPF-приложении можно просто определить собственный UI, используя стандартные подходы WPF, Windows и XAML Browser Application (XBAP), а затем динамически загрузить свой документ. На рис. 9 показан пример, в котором используется вымышленная библиотека моих статей с названиями, отображаемыми в списке в верхнем левом углу. Когда пользователь выбирает статью из списка, документ динамически загружается в элемент управления FlowDocumentReader, занимающий большую часть формы. Обратите внимание, что в этом решении используются стандартные приемы WPF, такие как альфа-смешивание. Вы увидите, что сам документ полупрозрачен и моя фотография на заднем плане просвечивает сквозь него. Заметьте также, что приложение создает библиотеку вымышленных статей с применением списка, окна изображения, метки и элемента управления FlowDocumentReader.


Рис. 9. Применение элементов управления Listbox, Image, Label и FlowDocumentReader



Самое сложное в этом примере - загрузка документа в элемент управления для просмотра. Это делается при помощи класса System.Windows.Markup.XamlReader, обеспечивающего динамическую загрузку любого XAML-контента, включая документы нефиксированного формата (и не только их). Вот код, подключенный к событию смены выделения в окне списка:
documentReader.Document = (FlowDocument)XamlReader.Load(
    File.OpenRead(fileName));


Метод Load возвращает объект, поскольку корневой элемент в XAML-файле может представлять множество разных типов. В моем примере мне известно, что возвращаемое значение - FlowDocument, поэтому я просто преобразую тип и присваиваю этот документ свойству Document элемента FlowDocumentReader (его экземпляр в данном случае называется documentReader). Помните, что это лишь пример. В производственном коде обязательно понадобится обработка ошибок. Все, что вы знаете о WPF, применимо и к этому примеру. Так, элементы управления просмотра являются стандартными в WPF и поддерживают стили. То есть вы можете полностью изменить внешний вид UI-элементов, в том числе полосы масштабирования, переключателей режимов просмотра и элементов управления пролистыванием. (Единственный элемент, возможность управления которым ограничена, - это окно поиска, хотя вы можете просто не использовать его, если оно вам не нравится.)

Кроме того, хотя в моем примере показано Windows-приложение, то же приложение может быть развернуто как XBAP и запущено в браузере (опять же в предположении, что у пользователя установлен пакет .NET Framework 3.0). Обратите внимание, что наличия Microsoft Silverlight (ранее называвшегося WPF/E) недостаточно, так как Silverlight поддерживает лишь подмножество WPF и не поддерживает документы нефиксированного формата.

Создание документов нефиксированного формата


Как создаются документы нефиксированного формата? Конечно, разработчики всегда могут создавать их при помощи низкоуровневых средств, таких как XamlPad. Тем не менее в реальных условиях это маловероятно. Обычно документы нефиксированного формата создаются либо в редакторах WYSIWYG, либо посредством преобразования содержимого из существующего формата. Поскольку документы нефиксированного формата могут быть определены при помощи XAML, особенно легко преобразовывать существующие XML-документы. Но также возможно преобразование документов HTML и Word (хотя потребуется программирование, поскольку для этого пока нет готовых средств).

Для редактирования в режиме WYSIWYG подсистема WPF предоставляет готовый элемент управления - RichTextBox; в него встроена поддержка редактирования XAML-документов. Название элемента управления наводит на неверное предположение, будто он предназначен для работы с RTF. Но, хотя этот элемент управления поддерживает RTF, он в основном используется для документов нефиксированного формата. На самом деле он дублирует возможности элементов управления для просмотра документов нефиксированного формата за исключением того, что поддерживает редактирование. Некоторые даже скажут, что RichTextBox следует считать еще одним способом отображения документов нефиксированного формата.

Наберите следующий пример в XamlPad, чтобы увидеть RichTextBox в действии:
<RichTextBox
  xmlns='http://schemas.microsoft.com/
      winfx/2006/xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  <FlowDocument>
    <Paragraph>
      The quick brown fox jumps over the lazy dog.
    </Paragraph>
  </FlowDocument>
</RichTextBox>


Как и у элементов управления для просмотра, у RichTextBox есть свойство Document, которому можно автоматически присвоить документ нефиксированного формата. Это создает UI, очень схожий с элементом управления FlowDocumentScrollViewer за исключением того, что текст можно редактировать. Заметьте, что этот элемент управления всегда обрабатывает документы в режиме прокрутки. Способа редактировать документы нефиксированного формата с помощью RichTextBox в постраничном или многоколоночном режиме нет. Однако результатом редактирования является стандартный документ нефиксированного формата, который можно отобразить при помощи любого из средств просмотра, которые вы уже видели, в том числе в многоколоночном и постраничном режимах.

Одна из заслуживающих упоминания возможностей RichTextBox - встроенная проверка орфографии. Ее можно включить так:
<RichTextBox SpellCheck.IsEnabled="true">
  <FlowDocument>...</FlowDocument>
</RichTextBox>


На рис. 10 показана проверка орфографии на практике.


Рис. 10. RichTextBox с проверкой орфографии



Единственная сложность в работе с этим элементом управления - загрузка и сохранение. В большинстве случаев вы, вероятно, не станете сохранять содержимое RichTextBox в XAML UI, как в одном из предыдущих примеров. Вместо этого вы будете загружать и сохранять документ динамически. Операция загрузки текста в RichTextBox идентична загрузке документа в элемент управления для просмотра (см. выше). Сохранение документа - строго обратная операция: вы берете документ и сериализуете его в XAML, что может быть сделано так:
System.Windows.Markup.XamlWriter.Save(richTextBox.Document)


В результате вы получите XAML-код в виде строки, которую потом сможете сохранить в файле или в базе данных либо использовать любым способом, который вы можете себе представить.

Хотя элемент RichTextBox очень удобен, стоит сказать и о его недостатках. Тогда как документы нефиксированного формата представляют собой самую изощренную технологию для отображения документов на экране, элемент управления RichTextBox не так сложен. Он великолепно подходит для редактирования небольших документов и фрагментов текста, но вы не станете использовать его для написания книги, журнала или маркетинговой брошюры. Для таких форматов его средства отображения слишком просты, поскольку поддерживают разметку только для прокрутки (т. е. нет удобного визуального способа создания сложных разметок, о которых мы вскоре поговорим). Подход к сохранению документов зачастую также неудовлетворителен. Класс XmlWriter использует документ из памяти и преобразует его в XAML, но, к сожалению, он не следует многим концепциям, важным при массовом создании документов нефиксированного формата и, в частности, не поддерживает стили. В результате XAML точно сохраняет внешний вид документа, но зачастую очень велик и громоздок. Элемент управления RichTextBox, без сомнения, полезен, но не ждите от него чудес.

Исследование возможностей разметки


Теперь, когда вы знаете, как создавать и просматривать документы нефиксированного формата, вернемся к самим документам и рассмотрим еще несколько возможностей. Документы нефиксированного формата очень сложны, и исследование всех доступных возможностей выходит за рамки этой статьи, но есть несколько вещей, которые я хотел бы обсудить.

Одна из функций, которая всегда поражает меня, - оптимизация абзацев. Если она включена, то пытается как можно равномернее распределить пробелы в данном абзаце, значительно повышая удобство чтения. Оптимизация абзацев особенно хорошо работает в комбинации со встроенной функцией расстановки переносов, которая осуществляет расстановку переносов «на лету» для всего документа или отдельных абзацев.

Включить оптимизацию абзацев и расстановку переносов очень легко:
<FlowDocument IsOptimalParagraphEnabled="true"
    IsHyphenationEnabled="true">


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


Рис. 11. Оптимизация абзацев и расстановка переносов



Как вы уже видели, в элементе управления FlowDocumentReader применяется многоколоночный способ отображения текста. Это другая очень важная составляющая, поскольку людям неудобно читать одну строку по всей ширине экрана. Реальная ширина колонок варьируется в зависимости от нескольких факторов, таких как вся ширина для отображения, масштаб и заданная ширина колонки. По умолчанию ширина колонки документа нефиксированного формата в 20 раз больше размера шрифта, что при размере шрифта по умолчанию составляет примерно 300 аппаратно-независимых пикселов (31/8 дюйма на точном дисплее). Этот параметр по умолчанию легко переопределить:
<FlowDocument ColumnWidth="400">


Получатся колонки шириной порядка 400 пикселов. Однако есть другие факторы, влияющие на конечную ширину. При масштабе, например, в 50% ширина колонки будет лишь 200 пикселов. К тому же, ширину колонки стоит воспринимать в большей мере как минимальную. Если общая доступная ширина 900 пикселов, отображаемый результат содержит две колонки, достаточно широкие, чтобы заполнить все 900 пикселов, делая каждую из колонок шире, чем заданные 400 пикселов. Обычно это желательно, поскольку при этом результат вывода гораздо приятнее для глаз. Но, если вам не нравится такое поведение и вы хотите, чтобы колонки были точно 400 пикселов в ширину, то можете сделать ширину колонок неизменной:
<FlowDocument ColumnWidth="400" IsColumnWidthFlexible="false">


Теперь колонки ровно 400 пикселов в ширину (при масштабе в 100%), а остающееся место пустует.

Другим имеющим отношение к колонкам параметром, с которым вам, вероятно, захочется поиграть, является промежуток между колонками. Его можно настроить через свойство ColumnGap (этот параметр тоже основан на аппаратно-независимых пикселах):
<FlowDocument ColumnGap="25">


Еще одна настройка - параметр графления колонок, позволяющий задавать визуальный элемент, отображаемый между колонками. Рассмотрим этот пример (его результаты показаны на рис. 12):
<FlowDocument ColumnRuleWidth="5" ColumnRuleBrush="Red">


Рис. 12. Документ нефиксированного формата с простым графлением между колонками



Конечно, во многих публикациях документы не просто делятся на элементарные колонки. Зачастую присутствуют другие элементы, выпадающие из обычного размещения текста. Вы уже видели примеры этому с изображениями, помещаемыми внутрь документов. На рис. 9 показано размещение, принятое у иллюстраторов. Изображение помещается между двух колонок, а текст «обтекает» его; в результате изображение находится прямо посреди текста без значительного нарушения размещения любой из колонок. Это распространенный вариант разметки, который был просто недоступен в известных мне динамических средах экранного чтения до появления документов нефиксированного формата.

Ключом к созданию таких разметок является блок Figure, позволяющий определить содержимое, не следующее структуре остального документа. Размещение изображения внутри тега Figure - один из примеров, но элементы Figure можно использовать и по-другому, скажем, для определения заголовка во всю ширину документа:
<Paragraph>
  <Figure HorizontalAnchor="ContentLeft"
          VerticalAnchor="ContentTop"
          Width="1Content">
    <Paragraph FontSize="36" FontWeight="Bold">Go With
        The Flow</Paragraph>
  </Figure>
  Windows Presentation Foundation in Windows Vista
    provides a great set of features.
</Paragraph>


В этом коде Figure содержит другой абзац, используемый как текст заголовка. Заметьте, что есть удобные свойства, которые можно использовать для гибкого создания сложных документов. Например, рассмотрим ширину иллюстрации. Вместо установки ширины в определенное число пикселов я устанавливаю ее точно равной ширине содержимого, что обеспечивает автоматическую подстройку ширину иллюстрации.

Взгляните на рис. 13. Здесь заголовок (размещенный с помощью Figure) настроен так, чтобы он пересекал весь документ, что смещает все четыре колонки вниз. Изображение по горизонтали и вертикали привязано к центру страницы.


Рис. 13. Заголовок пересекает четыре колонки



Обратите внимание, что рисунки с шириной, заданной относительно содержимого, не обязаны быть такой же ширины, что и содержимое. Следующий пример устанавливает ширину рисунка в 75% от ширины содержимого:
<Figure Width="0.75Content">


Ширина может быть задана и относительно других параметров, например ширины колонок. В следующем примере рисунок всегда шириной в две колонки, если не отображается только одна колонка (в этом случае ширина уменьшается до одной колонки):
<Figure Width="2Column">


Конечно, высота рисунков может быть задана таким же способом (хотя часто рисунки просто растягиваются по высоте вместе с содержимым).

Другим важным параметром является позиция рисунка. Во фрагменте кода он привязан к левому краю по горизонтали и к верхнему краю по вертикали. Это означает, что рисунок появляется в верхнем левом углу текущей страницы контента независимо от того, где именно он определен. В этом примере рисунок и так был определен в качестве первого элемента документа, но даже если бы до этого заголовка было несколько абзацев, он был бы смещен вверх и влево из-за таких настроек. Фотографии на рис. 9 и 13 были помещены между колонок аналогичным способом - установкой горизонтальной привязки в значение «PageCenter». (Доступные значения свойств для всех этих параметров см. в документации WPF.)

Из-за самой природы документов нефиксированного формата очень широко используются стили. Вы можете определять большую часть параметров форматирования в стилях, а не в атрибутах.

Вы могли заметить, что многое кодировалось вручную. Например, когда нужно изменить гарнитуру шрифта, вы добавляете эту информацию в блок или подставляемый в строку элемент. Пока что это не составляло большой проблемы, поскольку примеры в основном были невелики. Однако, если у вас есть 50-страничная глава книги и вы хотите изменить шрифт в каждом абзаце, то делать это каждый раз вручную становится утомительно. К счастью, есть подход получше: как и все остальное в WPF, документы нефиксированного формата поддерживают стили. Стили могут быть определены как именованные ресурсы в самом документе. Вот стиль, определяющий информацию о шрифте:
<FlowDocument>
  <FlowDocument.Resources>
    <Style x:Key="MyStyle">
      <Setter Property="TextElement.FontSize" Value="12" />
      <Setter Property="TextElement.FontFamily"
         Value="Bodoni MT" />
    </Style>
  <FlowDocument.Resources>
  ...
</FlowDocument>


Стиль может затем быть применен к абзацам (и другим элементам) следующим образом:
<Paragraph Style="{StaticResource MyStyle}">The quick...
    </Paragraph>


Из-за самой природы документов нефиксированного формата очень широко используются стили. Я рекомендую во всех случаях, кроме простейших, определять большую часть параметров форматирования в стилях, а не в атрибутах отдельных подставляемых в строку элементов. Стили помогают сохранять компактность документов и облегчают их поддержку.

Заключение


Надеюсь, что эта статья дала вам чуточку больше, чем базовое представление о документах нефиксированного формата и их возможностях, и пробудила в вас интерес к ним. Есть еще более «продвинутые» возможности, с которыми стоит ознакомиться, включая сложные стили элементов управления просмотра, создание подклассов и расширение документов, блоков и подставляемых в строку элементов, управление правами на доступ к цифровой информации, рукописные аннотации и расширенное форматирование шрифтов.
Метки: WPF, FlowDocument, Xaml, XPS
Идентификатор статьи: 68
Дата занесения/обновления: 29.02.2008 12:10

Комментарии к статье

Ваш комментарий будет первым!
Имя:
Текст:
  mml?
Пожалуйста, подтвердите, что вы человек, введя значение выражения 77+24=