Оформление текста в WinRT

193

Казалось бы, объекты 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 (в этом случае между словами вставляются дополнительные пробелы, чтобы текст равномерно распределялся по ширине).

Программа может запускаться как в книжной, так и в альбомной ориентации:

Вывод многострочного текста в приложении WinRT

Если экран реагирует на изменения ориентации, текст автоматически переформатируется. Windows Runtime разбивает строки по пробелам и дефисам, но не по неразрывным пробелам (&#x00A0) или неразрывным дефисам (&#x2011). «Мягкие» переносы (&#x00AD) игнорируются.

Не все элементы 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>

Результат выглядит следующим образом:

Использование двух элементов TextBlock внутри Grid

Обратите внимание: второй элемент визуально расположен поверх первого. Визуальный порядок наложения часто называется «Z-порядком», потому что в трехмерном координатном пространстве воображаемая ось Z выходит из экрана в направлении зрителя.

Конечно, наложение не может рассматриваться как обобщенное решение для вывода нескольких строк текста! Позже будет показано, как определять в Grid строки и столбцы для формирования макета. Другой подход к упорядочению множественных элементов в элементе Grid с одной ячейкой заключается в предотвращении перекрытия посредством использования разных значений HorizontalAlignment и VerticalAlignment для элементов внутри него.

Пройди тесты
Лучший чат для C# программистов