Главная » Silverlight » Метод ArrangeOverride ()

0

После получения размеров каждого элемента их можно разместить в доступном пространстве. Для этого надстройка Silverlight вызывает метод ArrangeOverride)) текущей панели, а панель вызывает метод Arrange О каждого дочернего элемента, чтобы сообщить ему, сколько пространства ему выделено. Метод Arrange () запуска­ет метод ArrangeOverride (), как и в предыдущем случае, когда Measure () запускает MeasureOverride ().

При вычислении размеров элемента с помощью метода Measure () ему передается объект Size, определяющий границы доступного пространства. При размещении эле­мента с помощью метода Arrange () ему передается объект System. Windows .Rect, опре­деляющий его размеры и позицию. В этот момент каждый элемент размещается как бы в стиле Canvas на основе координат X и Y, определяющих расстояние между левым верхним углом контейнера и элементом.

Ниже приведена базовая структура метода ArrangeOverride () без детализации кода, определяющего фактические размеры.

protected override Size ArrangeOverride (Size arrangeSize)

{

// Проход по всем дочерним элементам

foreach (UIElement element in this.Children) {

// Присвоение границ дочернему элементу Rect bounds = new Rect(…); element.Arrange(bounds);

И Теперь можно извлечь фактические размеры из //свойств element.ActualHeight и element.ActualWidth

}

// Вычисление пространства, занимаемого панелью; // оно будет использоваться для установки // свойств ActualHeight и ActualWidth панели return arrangeSize;

}

При размещении элементов передавать бесконечные размеры нельзя. Однако можно присваивать элементу желаемые размеры, передавая ему значение свойства DesiredSize. Кроме того, элементу можно предоставить больше пространства, чем он просит. Фактически это происходит довольно часто. Например, панель StackPanel по вертикали предоставляет дочернему элементу столько пространства, сколько он запро­сит, однако по горизонтали размер элемента ограничен шириной панели. Аналогично этому на панели Grid фиксированные или пропорциональные строки могут быть круп­нее, чем желаемые размеры элемента. И даже если элемент размещен в контейнере, размеры которого подгоняются под содержимое, элемент можно увеличить при условии явного задания размеров с помощью свойств Height и Width.

Когда размер элемента больше желаемого, вступают в игру свойства VerticalAlig­nment и HorizontalAlignment. С их помощью для содержимого элемента выбирается место в заданных границах.

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

Пользовательский контейнер Unif ormGrid

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

Примечание. Панель UniformGrid полезна как простая альтернатива решетке Grid. Она не требует явного определения строк и столбцов и размещения вручную каждого дочернего элемента в правильной ячейке. Поэтому панель UniformGrid имеет смысл использовать для вывода набора изображений. Платформа WPF содержит немного более сложную версию данного элемента управления.

Как и в случае любой пользовательской панели, создание UniformGrid начинается с объявления простого класса, наследующего базовый элемент управления Panel.

public class UniformGrid : System.Windows.Controls.Panel { . . . }

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

Концептуально, панель UniformGrid довольно простая. Она проверяет размеры до­ступного пространства, вычисляет количество и размеры необходимых ячеек и разме­щает дочерние элементы один за другим. Поведение панели UniformGrid настраивается с помощью двух свойств Rows и Columns, которые можно устанавливать независимо друг от друга или совместно.

public int Columns { get; set; }

public int Rows { get; set; }

Ниже описано, как свойства Rows и Columns влияют на алгоритм размещения до­черних элементов.

[1]    Если оба свойства установлены, панель UniformGrid вычисляет размеры ячеек. Для этого нужно всего лишь поделить размеры доступного пространства на ко­личество строк и столбцов. Если дочерних элементов больше, чем ячеек, лишние элементы не выводятся.

2  Если установлено одно из свойств (Rows или Columns), панель вычисляет другое свойство, предполагая, что должны быть выведены все дочерние элементы. На­пример, если Columns=3 и нужно разместить восемь элементов, панель создаст три строки.

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

Для реализации описанного выше поведения панель UniformGrid отслеживает ре­альное количество столбцов и строк. Оно равно свойствам Columns и Rows, если они установлены. Если нет, то для подсчета дочерних элементов и определения размеров решетки применяется пользовательский метод CalculateColumns (). Он может быть вы­зван на первом этапе размещения.

private int realColumns; private int realRows;

private void CalculateColumns () {

// Подсчет элементов; если элементов нет, II метод завершается

double elementCount = this.Children.Count; if (elementCount == 0) return;

realRows = Rows; realColumns = Columns;

II Использование свойств, если они установлены if ((realRows != 0) && (realColumns != 0)) return;

// Если ни одно свойство не установлено, // вычисляется количество столбцов if ((realColumns == 0) && realRows == 0) realColumns = (int)Math.Ceiling(Math.Sqrt(elementCount));

// Если установлено только свойство Rows, // вычисляется количество столбцов if (realColumns == 0) realColumns = (int)Math.Ceiling(elementCount / realRows);

// Если установлено только свойство Columns, // вычисляется количество строк if (realRows == 0) realRows =

(int)Math.Ceiling(elementCount / realColumns);

}

Надстройка Silverlight начинает процесс размещения, вызвав метод VeasureOverride () панели UniformGrid. Этот метод должен в свою очередь вызвать ме­тод, вычисляющий количество столбцов, и поделить доступное пространство поровну между одинаковыми ячейками.

protected override Size MeasureOverride (Size constraint) {

CalculateColumns();

// Предоставление пространства ячейкам Size childConstraint = new Size ( constraint.Width / realColumns, constraint.Height / realRows);

Теперь нужно измерить дочерние элементы. Необходимо учитывать, что метод Measure () может вернуть большее значение, если минимальный размер больше, чем помещается в доступном пространстве. Панель UniformGrid отслеживает наибольшие значения запрашиваемых ширины и высоты. И наконец, по завершении процесса из­мерения, панель вычисляет размеры пространства, необходимого для размещения эле­мента с максимальными шириной и высотой. Метод MeasureOverride () возвращает запрашиваемые размеры.

// Отслеживание максимальных запрашиваемых размеров Size largestCell = new Size () ;

// Проверка всех дочерних элементов

foreach (UIElement child in this.Children) {

// Получение желаемого размера для элемента child.Measure(childConstraint) ;

II Запись максимального запрашиваемого размера largestCell.Height = Math.Max(largestCell.Height,

child.DesiredSize.Height); largestCell.Width = Math.Max (largestCell.Width, child.DesiredSize.Width);

}

// Получение максимальных запрашиваемых размеров //и вычисление максимальных размеров решетки return new Size(largestCell.Width * realColumns, largestCell.Height * realRows);

}

Код метода ArrangeOverride () решает аналогичную задачу. Однако он не измеряет дочерние элементы. Вместо этого он получает окончательную разметку пространства, вы­числяет размеры ячеек и позиционирует каждый дочерний элемент в соответствующих границах. Если достигнут конец решетки, но еще остались дочерние элементы (это проис­ходит, только если потребитель элемента управления установил лимитирующие значения Rows и Columns), они передаются контейнеру размером 0x0, который скрывает их.

protected override Size ArrangeOverride(Size arrangeSize)

{

// Вычисление размеров каждой ячейки

double cellwidth = arrangeSize.Width / realColumns;

double cellHeight = arrangeSize.Height / realRows;

// Определение позиции каждой ячейки Rect childBounds =

new Rect(0, 0, cellWidth, cellHeight);

// Проверка всех элементов

foreach (UIElement child in this.Children) {

// Позиционирование дочерних элементов

child.Arrange(childBounds);

// Перемещение границ в следующую позицию childBounds.X += cellWidth;

if (childBounds.X >= cellWidth * realColumns) {

/ / Переход к следующей строке childBounds.Y += cellHeight; childBounds.X = 0;

// Если элементов больше, чем ячеек, // дополнительные элементы скрываются if (childBounds.Y >= cellHeight * realRows) childBounds = new Rect(0, 0, 0, 0) ;

}

}

// Возвращение размеров панели return arrangeSize;

Применить панель UniformGrid несложно. Нужно лишь отобразить пространство имен в разметку XAML и определить UniformGrid таким же образом, как и любой дру­гой контейнер. Ниже приведен пример размещения UniformGrid на панели StackPanel с дополнительным текстовым содержимым, позволяющим проверить, правильно ли вы­числены размеры UniformGrid, и убедиться в том, что последующее содержимое не на­слаивается на панель.

<UserControl x:Class="Layout.UniformGridTest" xmlns="http://schemas.microsoft.com/winfx/2006/

xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Layout" > <StackPanel Background="White"> <TextBlock Margin="5" Text="Coflepao<iMoe над UniformGrid"> </TextBlock>

clocal:UniformGrid Margin="5" Background="LawnGreen"> <Button Height="20" Content="короткая кнопка"> </Button>

<Button Width="150" Content=" длинная кнопка"»

</Button> <Button Width="80" Height="40" Content="Кнопка"> </Button> <TextBlock Margin="5" Text=

"Здесь размещен текст ячейки" TextWrapping="Wrap" Width="100"></TextBlock> <Button Width="80" Height="20"

Content="короткая кнопка"></Button> <TextBlock Margin="5" Техt"Дополнительный текст"

VerticalAlignment="Center"></TextBlock> <Button Content="Бeзpaзмepнaя "кнопка></Button> </local:UniformGrid>

<TextBlock Margin="5" Text="Содержимое под UniformGrid"> </TextBlock> </StackPanel> </UserControl>

На рис. 3.17 показан вывод приведенной выше разметки. Задавая разные размеры дочерних элементов UniformGrid, можно проверить, как работает панель. Например, в первой кнопке (Короткая кнопка) жестко закодировано свойство Height. В резуль­тате этого ее высота ограничена, а ширина автоматически становится равной шири­не ячейки. Во второй кнопке (Длинная кнопка) жестко закодировано свойство Width. Это наиболее широкий элемент в UniformGrid, поэтому ее ширина определяет шири­ну ячеек всей таблицы. В результате ширина и высота ячейки точно совпадают с раз­мерами кнопки Безразмерная кнопка — обе заполняют все доступное пространство ячейки. Больше всего пространства по вертикали занимают текстовые строки элемента TextBlock, поэтому они определяют высоту всех ячеек решетки.

Рис. 3.17. Панель UniformGrid

Примечание. Более сложную пользовательскую панель, которая размещает элементы по кругу и требует математических вычислений, можно найти по адресу www. tinyurl ,com/cwk6nz.

Источник: Мак-Дональд, Мэтью. Silverlight 3 с примерами на С# для профессионалов. : Пер. с англ. —- М. : ООО «И.Д. Вильяме», 2010. — 656 с. : ил. — Парал. тит. англ.

По теме:

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