Главная » Разработка для Windows Phone 7 » Silverlight и динамическая компоновка

0

 Ориентация

По умолчанию, программы на Silverlight для Windows Phone 7 выполняются в портретном режиме, а программы на XNA – в альбомном. В данной главе рассматривается, как изменить это поведение по умолчанию, и изучаются другие вопросы, касающиеся размеров экрана, размеров элементов и событий.

 

Если запустить приложение SilverlightHelloPhone из предыдущей главы и повернуть телефон или эмулятор на бок, обнаружится, что изображение не меняет расположения в зависимости от ориентации экрана. Это легко исправить. В корневом теге PhoneApplicationPage файла MainPage.xaml замените значение атрибута

SupportedOrientations="Portrait"

на:

SupportedOrientations="PortraitOrLandscape"

SupportedOrientations (Поддерживаемые ориентации) – это свойство PhoneApplicationPage. В качестве его значения может быть задан один из элементов перечисления SupportedPageOrientation (Поддерживаемые ориентации страницы): Portrait, Landscape или PortraitOrLandscape.

Выполняем компиляцию еще раз. Теперь при повороте телефона или эмулятора на бок соответствующим образом разворачивается и содержимое страницы:

Свойство SupportedOrientations в случае необходимости также позволяет обеспечить только альбомный режим отображения содержимого.

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

Двумя самыми важными свойствами при работе с динамической компоновкой являются HorizontalAlignment и VerticalAlignment. В предыдущей главе использовать эти свойства в

приложении на Silverlight для расположения текста по центру было, безусловно, проще, чем выполнять вычисления на основании размеров экрана и текста, как это требовалось в XNA.

С другой стороны, если сейчас вам будет поставлена задача разместить ряд строк текста, скорее всего, вы подсчитаете, что сделать это в XNA проще, чем в Silverlight.

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

А пока попытаемся разместить множество элементов в сетке для содержимого. Обычно Grid организует свое содержимое в ячейки, идентифицируемые строками и столбцами, но данная программа помещает девять элементов TextBlock в Grid с одной ячейкой для демонстрации использования свойств HorizontalAlignment и VerticalAlignment в девяти различных сочетаниях:

Проект Silverlight: SilverlightCornersAndEdges Файл: MainPage.xaml

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

<TextBlock Text="Top-Left"

VerticalAlignment="Top" HorizontalAlignment="Left" />

<TextBlock Text="Top-Center"

VerticalAlignment="Top" HorizontalAlignment="Center" />

<TextBlock Text="Top-Right"

VerticalAlignment="Top" HorizontalAlignment="Right" />

<TextBlock Text="Center-Left"

VerticalAlignment="Center" HorizontalAlignment="Left" />

<TextBlock Text="Center"

VerticalAlignment="Center" HorizontalAlignment="Center" />

<TextBlock Text="Center-Right"

VerticalAlignment="Center" HorizontalAlignment="Right" />

<TextBlock Text="Bottom-Left"

VerticalAlignment="Bottom" HorizontalAlignment="Left" />

<TextBlock Text="Bottom-Center"

VerticalAlignment="Bottom" HorizontalAlignment="Center" />

<TextBlock Text="Bottom-Right"

VerticalAlignment="Bottom" HorizontalAlignment="Right" />

</Grid>

Как и во многих более простых приложениях на Silverlight этой книги, я задал свойству SupportedOrientations объекта MainPage значение PortraitOrLandscape. И вот, как это выглядит, если развернуть телефон или эмулятор:

Кажется, что на экране представлены все возможные сочетания. На самом деле здесь не отображены настройки по умолчанию для свойств HorizontalAlignment и VerticalAlignment. По умолчанию в качестве значений этих свойств применяются элементы перечисления Stretch (Растянуть). При использовании значений по умолчанию TextBlock расположится в верхнем левом углу, так как если бы были заданы значения Top (Сверху) и Left (Слева). Но не так очевидно будет то, что TextBlock занимает всю площадь Grid. TextBlock имеет прозрачный фон (и изменить его никак нельзя), поэтому заметить разницу не так просто, но я продемонстрирую это в следующей главе.

Несомненно, свойства HorizontalAlignment и VerticalAlignment играют важную роль в системе компоновки в Silverlight. Это же можно сказать о свойстве Margin (Поле). Добавим Margin в первый TextBlock этого приложения:

<TextBlock Text="Top-Left"

VerticalAlignment="Top" HorizontalAlignment="Left" Margin="100" />

Теперь между TextBlock и левым и верхним краями клиентской области имеется отступ в 100 пикселов. Свойство Margin типа Thickness (Толщина) – это структура с четырьмя свойствами: Left, Top, Right (Справа) и Bottom (Снизу). Если в XAML задать всего одно число, это значение будет использоваться для всех четырех сторон. Также можно задать два значения:

Margin="100 200"

Первое из них применяется к правому и левому полям, второе – к нижнему и верхнему. Если задано четыре значения

Margin="100 200 50 300"

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

Если обоим свойствам, HorizontalAlignment и VerticalAlignment, задать значение Center (Центр), и задать для Margin четыре разных числа, визуально текст уже не будет находиться в центре области содержимого. Silverlight выполняет центрирование на основании размера элемента, включая поля.

TextBlock также имеет свойство Padding (Отступ):

<TextBlock Text="Top-Left"

VerticalAlignment="Top" HorizontalAlignment="Left" Padding="100 200" />

Padding также типа Thickness, и при использовании с TextBlock визуально невозможно отличить Padding от Margin. Но это, несомненно, разные вещи: Margin – это пространство вне

TextBlock, Padding – пространство внутри TextBlock, не занятое текстом. При использовании TextBlock для обработки событий касания (как будет показано в следующей главе), он будет реагировать на касания в области Padding, тогда как касания в области Margin будут игнорироваться.

Свойство Margin определяется в классе FrameworkElement. В реальных приложениях на Silverlight практически всем элементам задается Margin, отличное от нуля. Это предотвращает наложение элементов друг на друга. Свойство Padding используется реже. Оно задается только для TextBlock, Border (Рамка) и Control.

Margin может использоваться для позиционирования множества элементов в Grid с одной ячейкой. Обычно так не делают, для этого есть намного более удобные способы, но это возможно. Я приведу такой пример в главе 5.

Важно понимать, чего мы не делам. Мы не задаем явно Width и Height элемента TextBlock, как это происходит в некоторых устаревших программных средах:

<TextBlock Text="Top-Left"

VerticalAlignment="Top" HorizontalAlignment="Left" Width="100" Height="50" />

Мы угадываем размер TextBlock, не располагая той информацией об элементе, какой располагает сам TextBlock. В некоторых случаях Width и Height необходимо задавать, но не здесь.

Свойства Width и Height типа double. Значениями по умолчанию для них являются специальные значения с плавающей точкой Not a Number[2] или NaN. Если требуется получить фактическую ширину и высоту элемента, когда он отображается на экране, необходимо обратиться к свойствам ActualWidth (Фактическая ширина) и ActualHeight (Фактическая высота). (Но, внимание, эти свойства будут иметь ненулевые значения, только после того, как элемент выведен на экран.)

Для получения сведений, касающихся размеров элементов, предоставляются некоторые полезные события. Событие Loaded (Загружен) формируется, когда визуальные элементы впервые выведены на экран. Событие SizeChanged (Размер изменен) поддерживается элементами для оповещения об изменении ими размера. Событие LayoutUpdated (Компоновка обновлена) используется для оповещения об изменении компоновки, как это происходит при изменении ориентации.

Проект SilverlightWhatSize демонстрирует применение метода SizeChanged для отображения размеров нескольких элементов на стандартной странице. Необходимость в таких точных значениях размеров возникает не часто, но порой они могут представлять интерес.

Событие может быть ассоциировано с обработчиком события прямо в XAML, но сам обработчик события должен быть реализован в коде. При вводе имени события в XAML (например, SizeChanged) Visual Studio предложит создать обработчик события. Это и было сделано мною с событием SizeChanged сетки для содержимого:

Проект Silverlight: SilverlightWhatSize Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" SizeChanged="ContentPanel_SizeChanged"> <TextBlock Name="txtblk"

HorizontalAlignment="Center"

VerticalAlignment="Center" />

</Grid>

Свойству Name (Имя) элемента TextBlock присвоено значение «txtblk». Свойство Name играет очень особую роль в Silverlight. Если сейчас мы выполним компиляцию приложения и заглянем в файл MainPage.g.cs – файл кода, автоматически формируемый компилятором на основании файла MainPage.xaml – то увидим в классе MainPage ряд полей, среди которых будет и поле txtblk типа TextBlock:

internal System.Windows.Controls.TextBlock txtblk;

Также мы заметим, что данное значение этому полю задается программно в методе InitializeComponent:

this.txtblk = ((System.Windows.Controls.TextBlock)(this.FindName("txtblk")));

Это означает, что в любой момент после вызова метода InitializeComponent конструктором MainPage.xaml.cs любой код класса MainPage может ссылаться на этот элемент TextBlock в файле XAML, используя переменную txtblk, которая хранится как поле класса.

В файле MainPage.xaml обратите внимание, что некоторым элементам имена присваиваются с использованием синтаксиса x:Name, а не Name. В XAML эти два атрибута практически эквивалентны. Только Name применяется исключительно для элементов (т.е. экземпляров классов, производных от FrameworkElement, потому что именно в нем описано свойство Name), а x:Name годится для всего.

Это означает, что в коде класса MainPage в файле MainPage.xaml.cs имеется поле ContentPanel, предусмотренное для ссылки на стандартный Grid из MainPage.xaml, и то же самое для остальных элементов MainPage.xaml.

Присваивание имен элементам – один из двух основных способов взаимодействия кода и XAML. Второй способ – обработка в коде событий, которые формируются элементами, описанными в XAML. Рассмотрим обработчик события SizeChanged сетки для содержимого, который Visual Studio формирует автоматически:

Проект Silverlight: SilverlightWhatSize Файл: MainPage.xaml.cs (фрагмент)

private void ContentPanel SizeChanged(object sender, SizeChangedEventArgs e) {

}

Мне не нравятся обработчики, создаваемые Visual Studio. Как правило, я удаляю ключевое слово private, переименовываю обработчики событий так, чтобы их имена начинались со слова On, и убираю подчеркивания. Этот обработчик я назвал бы OnContentPanelSizeChanged (При изменении размера панели для содержимого). Также обычно я заменяю аргументы событий e на args.

Но для данного приложения я оставлю все как есть. Первым в этот метод передается аргумент sender (отправитель). Это элемент, сформировавший событие, которым в данном случае является Grid под именем ContentPanel. Второй аргумент включает данные, касающиеся конкретного события.

Я добавил тело этого метода, в котором свойству Text элемента txtblk просто присваивается более длинная строка, состоящая из нескольких строк:

Проект Silverlight: SilverlightWhatSize Файл: MainPage.xaml.cs (фрагмент)

private void ContentPanel_SizeChanged(object sender, SizeChangedEventArgs e) {

txtblk.Text = String.Format("ContentPanel size: {0}\n" +

"TitlePanel size: {1}\n" + "LayoutRoot size: {2}\n" + "MainPage size: {3}\n" + "Frame size: {4}", e.NewSize,

new Size(TitlePanel.ActualWidth,

TitlePanel.ActualHeight),

new Size(LayoutRoot.ActualWidth,

LayoutRoot.ActualHeight),

new Size(this.ActualWidth, this.ActualHeight), Application.Current.RootVisual.RenderSize);

}

Эти пять элементов типа Size, который является структурой со свойствами Width и Height. Размер самого ContentPanel доступен через свойство NewSize (Новый размер) аргументов события. Для следующих трех элементов я использовал свойства ActualWidth и ActualHeight.

Обратите внимание на последний элемент. Статическое свойство Application.Current возвращает объект Application, ассоциированный с текущим процессом. Это объект App, созданный программой. Он имеет свойство RootVisual (Корневой визуальный элемент), которое ссылается на рамку, но определено типа UIElement. Свойства ActualWidth и ActualHeight описаны FrameworkElement; это класс, наследуемый от UIElement. Вместо приведения я решил использовать свойство типа Size, описываемое классом UIElement.

Первое событие SizeChanged возникает при создании страницы и расстановке ее элементов, т.е. когда сетка для содержимого меняет свой размер от 0 до некоторого конечного значения:

Обратите внимание, что рамка не меняет ориентацию. В альбомном режиме панель задач занимает 72 пиксела ширины MainPage.

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии