Оформление текста в WinRT
193WinRT --- Оформление текста
Казалось бы, объекты Grid, TextBlock и Image уместно называть элементами управления (UI controls), поскольку эти классы находятся в пространстве имен Windows.UI.Xaml.Controls. Но формально они не относятся к элементам управления. В Windows Runtime определен класс с именем Control, но эти классы не наследуют от Control. Ниже приведен фрагмент иерархии Windows Runtime с классами, встречавшимися нам в проекте ранее:
Object DependencyObject UIElement FrameworkElement TextBlock Image Panel Grid Control UserControl Page
Различия между элементом (element) и элементом управления (control) не всегда очевидны, но они достаточно важны. На визуальном уровне элементы управления строятся из элементов, а визуальное оформление элемента может настраиваться при помощи шаблона. Grid также является элементом, но чаще обозначается термином «панель», и это (как вы вскоре убедитесь) очень существенное различие.
Проведем эксперимент: в исходном проекте WinRTTestApp, который мы создали ранее, переместите атрибут Foreground и все атрибуты, относящиеся к шрифтам, из элемента TextBlock в Page. Файл MainPage.xaml должен выглядеть так:
<Page
x:Class="WinRTTestApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinRTTestApp"
FontSize="80"
FontStyle="Italic"
FontFamily="Arial"
Foreground="LimeGreen">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="Привет, Windows 8!"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
</Page>
Вы увидите, что результат совершенно не изменился. Когда эти атрибуты устанавливаются для элемента Page, они распространяются на все, что находится на странице. Теперь попробуйте назначить свойству Foreground элемента TextBlock красный цвет:
<TextBlock Text="Привет, Windows 8!"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Red"
/>
Локально назначенный красный цвет заменяет зеленый цвет, установленный для Page.
Page, Grid и TextBlock образуют так называемое «визуальное дерево» элементов (впрочем, в файле XAML это дерево перевернуто): класс Page - ствол дерева, а его потомки (Grid и TextBlock) образуют ветви. Значения шрифтовых свойств и свойства Foreground, определенные для Page, распространяются вниз по визуальному дереву от родителя к потомкам. При этом имеется одна особенность: в Grid этих свойств нет. Эти свойства определяются в TextBlock и отдельно определяются в Control; это означает, что свойства каким-то образом передаются из Page в TextBlock, несмотря на наличие промежуточного элемента с совершенно другим строением.
Заглянув в документацию этих свойств в классе TextBlock и Page, вы увидите, что они там приводятся дважды под несколько различающимися именами. В документации TextBlock встречается свойство FontSize типа double:
public double FontSize { get; set; }
Также там присутствует свойство FontSizeProperty с типом DependencyProperty:
public static DependencyProperty FontSizeProperty { get; }
Обратите внимание: свойство FontSizeProperty является статическим и доступно только для чтения.
Многие классы, используемые при конструировании пользовательского интерфейса в приложениях Windows 8, наряду с обычными свойствами также содержат соответствующие свойства, называемые свойствами зависимостей (dependency properties) типа DependencyProperty. Интересно, что в только что представленной иерархии имеется класс с именем DependencyObject. Эти два типа взаимосвязаны: класс, производный от DependencyObject, часто объявляет статические, доступные только для чтения свойства типа DependencyProperty. И DependencyObject, и DependencyProperty определяются в пространстве имен Windows.UI.Xaml; это показывает, насколько важную роль они играют в системе.
Свойства зависимостей предназначены для решения некоторых принципиальных проблем, возникающих в сложных пользовательских интерфейсах. В приложениях Windows 8 свойства могут задаваться разными способами. Например, вы уже видели, что свойства могут назначаться непосредственно для объектов или наследоваться по визуальному дереву. Свойства также могут задаваться в определении Style со стилями. В последующих статьях приводятся примеры задания свойств для анимаций. Классы DependencyObject и DependencyProperty являются частью системы, которая помогает сохранить порядок в такой среде за счет установления приоритетов разных способов задания свойств. Пока я не стану углубляться в подробное описание этого механизма; вы познакомитесь с ним, когда займетесь созданием собственных элементов управления.
Формально свойство FontSize поддерживается свойством зависимости с именем FontSizeProperty, но иногда вследствие семантического сокращения само свойство FontSize называется свойством зависимости. Как правило, такие сокращения обходятся без недоразумений.
Многие свойства, определяемые UIElement и производными классами, являются свойствами зависимостей, но лишь немногие из них распространяются по визуальному дереву. В частности, к этой категории относится свойство Foreground и все шрифтовые свойства, а также ряд других, на которые я непременно обращу ваше внимание, когда о них пойдет речь. Свойства зависимостей также имеют встроенные значения по умолчанию. Если убрать все атрибуты TextBlock и Page, кроме Text, то текст будет выводиться в левом верхнем углу страницы системным шрифтом белого цвета с размером 11 пикселов.
Свойство FontSize задается в пикселах и определяет расчетную высоту шрифта, включающую место для подстрочных выносных элементов и диакритических знаков. Как вам, вероятно, известно, размер шрифта часто задается в пунктах (points) - в системах электронной верстки эта единица соответствует 1/72 дюйма. Соответствие между пикселами и пунктами называется разрешением экрана и измеряется в точках на дюйм (DPI, Dots Per Inch). При отсутствии дополнительной информации обычно считается, что экран монитора имеет разрешение 96 DPI, поэтому высота шрифта с размером 96 пикселов равна 72 пунктам (один дюйм), а у используемого по умолчанию шрифта с размером 11 пикселов высота составляет 8¼ пункта.
Для экранов высокого разрешения Windows автоматически пересчитывает размеры и координаты. Приложение может получить эту информацию от класса DisplayProperties, который занимает центральное место в пространстве имен Windows.Graphics.Display. Впрочем, в большинстве случаев предположение о разрешении экрана 96 DPI работает нормально, поэтому я обычно использую размеры в пикселах, соответствующие простым долям дюйма: 48, 24, 12 и 6 (соответственно 1/2, 1/4, 1/8 и 1/16 дюйма).
Вы уже видели, что при удалении атрибута Foreground выводится белый текст на черном фоне. Точнее, фон не совсем черный, но предопределенный идентификатор ApplicationPageBackgroundThemeBrush, используемый Grid, достаточно близок к нему.
В проект также включены два других файла, образующих пару: App.xaml и App.xaml.cs совместно определяют класс с именем App, производный от Application. Приложение может содержать несколько классов, производных от Page, но класс, производный от Application, может быть только один. Класс App отвечает за конфигурацию и выполнение операций, влияющих на приложение в целом.
Проведем эксперимент: в корневом элементе файла App.xaml задайте атрибуту RequestedTheme значение Light:
<Application
x:Class="WinRTTestApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinRTTestApp"
RequestedTheme="Light">
...
</Application>
Вариантов всего два: Light и Dark. Если теперь перекомпилировать и запустить программу, в ней будет использоваться светлый фон, то есть идентификатор ApplicationPageBackgroundThemeBrush соответствует другому цвету. Если свойство Foreground не задано явно, цвет текста станет черным; это означает, что с новой темой свойство Foreground имеет другое значение по умолчанию.
Выбор темы остается за вами, однако следует учитывать, что сейчас все больше компактных и полноразмерных устройств использует экраны на базе технологии OLED (органические светодиоды), а эти экраны потребляют меньше энергии при снижении уровня подсветки. Сокращение энергопотребления - одна из причин популярности темных цветовых схем. Конечно, вы можете определить собственные цвета, задав свойство Background элемента Grid и свойство Foreground элемента TextBlock:
<Grid Background="Gray">
<TextBlock Text="Привет, Windows 8!"
Foreground="LimeGreen"
...
/>
</Grid>
Для таких свойств функция Visual Studio IntelliSense предоставляет 140 стандартных названий цветов, а также значение Transparent. Все они являются статическими свойствами класса Color. Также можно задать цвет в формате RGB с префиксом # и шестнадцатеричными кодами цветов в диапазоне от 00 до FF:
Foreground="#33cc33"
Необязательный четвертый байт в начале определяет интенсивность альфа-канала со значениями в диапазоне от 00 (прозрачный) до FF (непрозрачный). Ниже показано определение зеленого цвета с половинной интенсивностью:
Foreground="#8033cc33"
Цвета, у которых в начале задан альфа-канал, иногда обозначаются сокращением ARGB. Класс UIElement также определяет свойство Opacity, которому задается значение в диапазоне от 0 (прозрачный) до 1 (непрозрачный). В приложении попробуйте задать свойству Background элемента Grid цвет, отличный от черного (например, Blue), а свойству Opacity элемента Image - значение 0.5, чтобы увидеть работу этого свойства.
При побайтовом задании цветов значения задаются по известной схеме sRGB («standard RGB»). Это цветовое пространство происходит из эпохи электронно-лучевых трубок, когда эти байты напрямую соответствовали напряжениям подсветки этих пикселов. К счастью, нелинейность яркости пикселов приблизительно компенсируется нелинейностью восприятия яркости человеческим глазом, так что значения байтов субъективно воспринимаются как линейные.
Альтернативное цветовое пространство scRGB использует значения в диапазоне от 0 до 1, пропорциональные интенсивности подсветки. Например, серому средней интенсивности соответствует следующее определение:
Foreground="sc# 0.5 0.5 0.5"
Из-за логарифмического восприятия интенсивности подсветки человеческим глазом этот серый цвет будет казаться слишком светлым для «среднего».
Если потребуется вывести символы, отсутствующие на клавиатуре, задайте их в формате Юникода с использованием стандартных служебных последовательностей XML. Например, если в приложении нужно вывести текст «Стоимость продукта: €50», а вы ограничены базовой клавиатурой, символ евро можно задать в десятичном формате:
Text="Стоимость продукта: €50"
А может, вы предпочитаете шестнадцатеричную кодировку:
Text="Стоимость продукта: €50"
Как и в стандартной кодировке XML, строки могут содержать специальные символы с префиксом &, ниже показаны некоторые из них:
& - амперсанд (&);
' - апостроф;
" - кавычка;
< - левая угловая скобка (знак «меньше»);
> - правая угловая скобка (знак «больше»).
Вместо задания свойства Text элемента TextBlock можно разделить элемент на начальный и конечный теги и задать текст как содержимое:
<TextBlock ...>
Привет, Windows 8!
</TextBlock>
Назначение текста как содержимого TextBlock не совсем эквивалентно заданию свойства Text. Впрочем, даже без учета дополнительных возможностей задание текста в содержимом удобно для вывода больших объемов текста, потому что оно создает меньше проблем с лишними пробелами, чем при использовании текста в кавычках. Ниже показан пример, который выводит целый абзац текста, заданный как содержимое тега TextBlock:
<TextBlock TextWrapping="Wrap" TextAlignment="Justify"
...>
Длинный текст..
</TextBlock>
В процессе разбора символы конца строки и пробелы в начале каждой строки сжимаются в один пробел. Обратите внимание на свойство TextWrapping, определяющее режим переноса текста. По умолчанию используется значение перечисляемого типа TextWrapping.NoWrap; единственная альтернатива - Wrap. Свойству TextAlignment, определяющему режим выравнивания текста, задаются значения из перечисления TextAlignment: Left, Right, Center или Justify (в этом случае между словами вставляются дополнительные пробелы, чтобы текст равномерно распределялся по ширине).
Программа может запускаться как в книжной, так и в альбомной ориентации:
Если экран реагирует на изменения ориентации, текст автоматически переформатируется. Windows Runtime разбивает строки по пробелам и дефисам, но не по неразрывным пробелам ( ) или неразрывным дефисам (‑). «Мягкие» переносы (­) игнорируются.
Не все элементы XAML поддерживают текстовое содержимое по образцу TextBlock. Например, элементы Page и Grid не могут иметь текстового содержимого. Синтаксис XAML не настолько либерален, как синтаксис HTML, потому что он базируется исключительно на классах и свойствах. При этом элемент Grid поддерживает множественные дочерние элементы TextBlock, как показано в примере ниже:
<Grid Background="Gray">
<TextBlock Text="8"
FontSize="800"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Foreground="LimeGreen"
FontWeight="Bold"
/>
<TextBlock Text="Windows"
FontSize="200"
FontStyle="Italic"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Foreground="DarkOrange"
/>
</Grid>
Результат выглядит следующим образом:
Обратите внимание: второй элемент визуально расположен поверх первого. Визуальный порядок наложения часто называется «Z-порядком», потому что в трехмерном координатном пространстве воображаемая ось Z выходит из экрана в направлении зрителя.
Конечно, наложение не может рассматриваться как обобщенное решение для вывода нескольких строк текста! Позже будет показано, как определять в Grid строки и столбцы для формирования макета. Другой подход к упорядочению множественных элементов в элементе Grid с одной ячейкой заключается в предотвращении перекрытия посредством использования разных значений HorizontalAlignment и VerticalAlignment для элементов внутри него.