Диспетчеры компоновки
67Expression Blend --- Графические интерфейсы --- Диспетчеры компоновки
Как пояснялось ранее, при создании нового проекта приложения WPF или Silverlight в среде Expression Blend IDE в качестве используемого по умолчанию диспетчера компоновки автоматически предоставляется объект типа Grid. Напомним, что этот объект называется LayoutRoot, хотя его имя нетрудно изменить помощью свойства Name. Так, если создать новый проект приложения WPF, в среде Expression Blend IDE будет автоматически сформирована приведенная ниже разметка в коде XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="UI1_WPF.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot"/>
</Window>
Предоставляемый по умолчанию объект типа Grid вполне подходит для большинства приложений на платформе WPF или Silverlight, но имеются и другие диспетчеры компоновки. Оказывается, что в прикладном интерфейсе WPF API предоставляется более широкий выбор диспетчеров компоновки, чем в прикладном интерфеисе Silverlight API. Но на практике это редко вызывает какие-либо осложнения в работе над проектами.
Напомним, что приложение Silverlight, как правило, разрабатывается лишь как малая часть более крупной веб-страницы и практически не изменяет свои исходные размеры в окне браузера, где оно размещается. Именно поэтому на платформе Silverlight отсутствуют диспетчеры компоновки, динамически переставляющие элементы пользовательского интерфейса в зависимости от их размеров.
С другой стороны, приложения WPF чаще всего разрабатываются таким образом, чтобы объекты типа Window можно было разворачивать, сворачивать и изменять их размеры подобно обычным окнам. Именно по этой причине в прикладном интерфейсе WPF API предоставляется целый ряд дополнительных средств для динамической компоновки пользовательского интерфейса, которые описаны ниже.
Диспетчер компоновки | Поддержка в WPF API | Поддержка в Silverlight API | Назначение |
---|---|---|---|
Canvas | + | + | Располагает порожденные объекты в абсолютных координатах X, Y. Идеально подходит для хранения сложных графических данных |
DockPanel | + | + | Пристыковывает порожденные объекты к указанному (верхнему, нижнему, левому, правому) краю контейнера |
Grid | + | + | Определяет ряд ячеек сетки (по строкам и столбцам) для расположения порожденных объектов |
ScrollViewer | + | Прокручивает содержащиеся в нем элементы. Такой диспетчер компоновки может содержать только один элемент пользовательского интерфейса, который почти всегда оказывается еще одним диспетчером компоновки | |
StackPanel | + | + | Располагает порожденные объекты на одной вертикальной или горизонтальной линии |
UniformGrid | + | Располагает порожденные объекты равномерной сеткой, как, например, в игре в крестики-нолики | |
ViewBox | + | Масштабирует все порожденные объекты подобно инструменту изменения масштаба. Как и диспетчер компоновки типа ScrollViewer, почти всегда содержит подчиненный диспетчер компоновки | |
WrapPanel | + | Располагает порожденные объекты рядами слева направо. По достижении правого края панели остальные объекты автоматически переносятся на следующую строку и так далее слева направо и сверху вниз |
Дополнительные типы диспетчеров компоновки
Помимо основных диспетчеров компоновки, перечисленных в таблице, на платформах WPF и Silverlight предоставляются классы Border и TabControl. Как подразумевают их имена, в классе Border определяются видимые границы (и дополнительно фон) для порожденных объектов, связанных одним и тем же расположением, тогда как в классе TabControl предоставляются средства для расположения элементов пользовательского интерфейса на независимых панелях, снабженных вкладками.
Кроме того, в прикладном интерфейсе WPF API предоставляется элемент управления типа BulletDecorator, позволяющий связывать вместе два элемента пользовательского интерфейса (как правило, графику и фрагмент текста) для быстрого построения специальных маркированных списков. Преимущество такого группирования связанного вместе содержимого в едином элементе управления типа BulletDecorator, а не в автономном и пустом диспетчере компоновки типа StackPanel заключается в том, что у данного элемента управления имеются свойства для независимого (и простого) взаимодействия с графическим изображением маркера абзаца через одноименное свойство Bullet.
Так или иначе, работая с любым типом диспетчера компоновки, в том числе и с таким простым, как BulletDecorator, следует иметь в виду, что подходящую компоновку по определенной системе можно создать самыми разными способами, в частности, можно активизировать режим виртуального полотна на сетке, представленной объектом типа Grid, чтобы добиться абсолютного расположения содержимого в зависимости от размеров и полей, установленных для элементов пользовательского интерфейса из этого содержимого. С другой стороны, ту же самую компоновку можно создать с помощью коллекции вложенных объектов типа StackPanel, расположенных с разной ориентацией.
Непременно уделите время экспериментированию с каждым типом диспетчеров компоновки и проверке получаемых результатов, изменяя размеры, а возможно, и положение этих контейнеров содержимого во время выполнения. Далее вам предстоит работать с целым рядом диспетчеров компоновки в контексте более крупного приложения, разрабатываемого в среде Expression Blend, а до тех пор рассмотрим самые основные способы их применения.
Смена типа диспетчера компоновки
После создания нового проекта приложения WPF или Silverlight в среде Expression Blend IDE можете изменить тип любого диспетчера компоновки. Для этого щелкните правой кнопкой мыши на узле выбранного диспетчера компоновки в иерархическом представлении на панели Objects and Timeline и выберите команду Change Layout Type (Сменить тип компоновки) из всплывающего контекстного меню.
Если вы сменяете диспетчеры компоновки, содержащие элементы управления, имейте в виду, что эти элементы управления подчиняются правилам компоновки, установленным для их контейнера! Так, если вы расположите графические элементы на сетке, представленной объектом типа Grid, а затем смените его на объект типа StackPanel, все эти элементы расположатся друг за другом.
![Смена типа диспетчера компоновки](files/img24011.jpg)
Конструирование вложенных компоновок
В приложениях на платформе WPF или Silverlight очень часто применяется система вложенной компоновки. В частности, можно разделить исходную сетку типа Grid на два столбца, расположить в левом столбце вертикальную блочную панель типа StackPanel, а в правом столбце — виртуальное полотно типа Canvas, чтобы построить систему компоновки для простой программы раскраски.
Когда же требуется добавить порожденные диспетчеры компоновки ниже родительского диспетчера компоновки, выберите сначала родительский узел на панели Objects and Timeline, затем порожденный узел для добавления в области Layout на панели Tools и дважды щелкните на нужном элементе.
Группирование и разгруппирование выбранных элементов пользовательского интерфейса
Выделение на монтажном столе и группирование выбранных элементов пользовательского интерфейса в новый диспетчер компоновки, вложенный в родительский диспетчер, осуществляется в Expression Blend довольно просто. Допустим, имеется объект типа Grid, содержащий три объекта типа Button, которые требуется расположить рядом в объекте типа Grid. Если выбрать все три объекта на монтажном столе (для чего достаточно нажать клавишу <Shift> и щелкнуть сначала на первом объекте, а затем на последнем), то далее остается лишь щелкнуть правой кнопкой мыши на выделенных объектах и выбрать команду Group Into (Сгруппировать) из всплывающего контекстного меню.
![Группирование выбранных объектов в диспетчер компоновки](files/img24012.jpg)
Несколько элементов пользовательского интерфейса можно также выбрать на панели Objects and Timeline и затем активизировать команду меню Group Into. Если предположить, что три объекта типа Button сгруппированы в диспетчер компоновки типа StackPanel, то их иерархическое представление на панели Objects and Timeline будет выглядеть так, как показано на рисунке ниже. Однако новый диспетчер типа StackPanel для данной компоновки может быть выбран независимым образом на панели Properties:
![Простая система вложенной компоновки](files/img24013.jpg)
Для расформирования системы вложенной компоновки достаточно выбрать сначала диспетчер компоновки на монтажном столе и на панели Objects and Timeline, а затем щелкнуть на нем правой кнопкой мыши и выбрать команду Ungroup из всплывающего контекстного меню. В итоге порожденный диспетчер компоновки будет удален из иерархического представления в разметке XAML, а содержавшиеся в нем элементы пользовательского интерфейса будут размещены непосредственно в родительском диспетчере компоновки.
Перестановка элементов пользовательского интерфейса в диспетчерах компоновки
Если для пользовательского интерфейса главного окна (объекта типа Window) приложения WPF или начальной веб-страницы (объекта типа UserControl) приложения Silverlight построена система вложенной компоновки, то в ней иногда возникает потребность в перестановке отдельных элементов. Продолжая рассмотренный выше пример, допустим, что, после того, как объекты типа Button были сгруппированы в новый диспетчер компоновки типа StackPanel, крайний справа объект типа Button решено расположить в родительском диспетчере компоновки типа Grid.
Подобную перестановку можно, в частности, сделать, выбрав нужный объект типа Button на монтажном столе и перетащив его за пределы текущего контейнера в требуемый внешний контейнер, а затем нажав клавишу <Alt>, аналогично тому, как это делалось при создании составного содержимого:
![Перемещение элемента в родительский диспетчер компоновки](files/img24014.jpg)
Элементы пользовательского интерфейса можно также перетаскивать из одного диспетчера компоновки в другой непосредственно на панели Objects and Timeline. О такой возможности полезно всегда помнить, поскольку после добавления нового элемента из панели Tools на монтажный стол он будет содержаться в выбранном в настоящий момент диспетчере компоновки. Поэтому если случайно выбрать не тот диспетчер компоновки перед добавлением очередного элемента пользовательского интерфейса, то эту ошибку нетрудно исправить, перетащив узел данного объекта в нужный контейнер.