Главная » Разработка для Windows Phone 7 » Пользовательский вертикальный StackPanel

0

Рассмотрим еще один производный от Panel класс – StackPanel – и вы увидите, как он отличается от Grid с одной ячейкой. Чтобы не усложнять код и избежать описания свойств, я назову этот пользовательский класс VerticalStackPanel. Вот метод MeasureOverride:

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

protected override Size MeasureOverride(Size availableSize) {

Size compositeSize = new Size();

foreach (UIElement child in Children) {

child.Measure(new Size(availableSize.Width, Double.PositiveInfinity)); compositeSize.Width = Math.Max(compositeSize.Width, child.DesiredSize.Width);

compositeSize.Height += child.DesiredSize.Height;

}

return compositeSize;

}

Как обычно, метод MeasureOverride перебирает все дочерние элементы и вызывает Measure для каждого из них. Но заметьте, что в данном случае предлагаемый каждому дочернему элементу Size включает ширину самого VerticalStackPanel и не ограничен по высоте.

По сути, у дочерних элементов спрашивают, какой высоты они должны быть. Для TextBlock высоту определить просто: она равна высоте текста. Для Ellipse тоже нет ничего сложного: она равна нулю. А вот элемент Image вычисляет высоту по заданной ширине, исходя из неизменных пропорций изображения. Полученное значение высоты может отличаться от высоты Grid с одной ячейкой.

Как и в перегруженном MeasureOverride класса SingleCellGrid, свойство Width локальной переменной compositeSize основывается на максимальной ширине дочернего элемента. А вот свойство Height переменной compositeSize в данной панели является накапливаемым. Высота VerticalStackPanel должна быть равна сумме высот всех его дочерних элементов.

Если сам VerticalStackPanel располагается в StackPanel с горизонтальной ориентацией, свойство Width переменной availableSize будет иметь бесконечное значение, и Measure будет

вызываться для каждого дочернего элемента, размер которого бесконечен в обоих направлениях. В этом нет ничего страшного и ничего такого, что требовало бы какого-то особого подхода.

В SingleCellGrid метод ArrangeOverride разместил все дочерние элементы в одном и том же месте. Панели VerticalStackPanel необходимо выстроить дочерние элементы в ряд друг над другом. Для этого он определяет локальные переменные x и y:

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

protected override Size Arrange0verride(Size finalSize) {

double x = 0, y = 0;

foreach (UIElement child in Children) {

child.Arrange(new Rect(x, y, finalSize.Width, child.DesiredSize.Height)); y += child.DesiredSize.Height;

}

return base.Arrange0verride(finalSize);

}

Переменная x всегда остается равной 0, а в переменной суммируются значения свойства Height размера DesiredSize каждого дочернего элемента. При вызове метода Arrange в него передаются x и у, указывающие на местоположение дочернего элемента относительно верхнего левого угла панели. Свойство Width этого Rect равно свойству Width finalSize, но свойство Height равно значению Height DesiredSize дочернего элемента. Это значение соответствует величине пространства по вертикали, выделенного для каждого дочернего элемента ранее в методе MeasureOverride. Предоставление дочернему элементу его собственной желаемой высоты в методе Arrange, по сути, аннулирует любое значение VerticalAlignment, заданное для дочернего элемента. Этот эффект был обнаружен нами ранее опытным путем при рассмотрении вертикального StackPanel.

В общем, если дочернему элементу в методе MeasureOverride предлагается неограниченный размер по вертикали или по горизонтали, или в обоих направлениях, этот размер дочернего элемента будет определен в методе ArrangeOverride исходя из DesiredSize.

Файл MainPage.xaml проекта VerticalStackPanelDemo (Демонстрация вертикальной стек- панели) аналогичен приводимому в начале данной главы, только в нем используется VerticalStackPanel:

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

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

<TextBlock Text="TextBlock aligned at right bottom" HorizontalAlignment="Right" VerticalAlignment="Bottom" />

<Image Source="Images/BuzzAldrin0nTheMoon.png" />

<Ellipse Stroke="{StaticResource PhoneAccentBrush}" StrokeThickness="2 4" />

<TextBlock Text="TextBlock aligned at left top" HorizontalAlignment="Left" VerticalAlignment="Top" /> </local:VerticalStackPanel> </Grid>

На экране получаем тот же вывод, что и в предыдущем приложении.

Если VerticalStackPanel располагается в сетке для содержимого, в его метод MeasureOverride передаются размеры, равные размерам самой сетки (за вычетом размера Margin, если он задан для VerticalStackPanel). Это конечный размер, который мы обсуждали в приложении SilverlightWhatSize в главе 2.

Но поместите VerticalStackPanel (или вертикальный StackPanel) в ScrollViewer, и эффект будет совершенно иным. По умолчанию ScrollViewer отображает вертикальную полосу прокрутки, поэтому ScrollViewer (или, скорее, один из его дочерних элементов) вызывает Measure элемента StackPanel с конечной шириной, но бесконечной высотой. После этого DesiredHeight вертикального StackPanel предоставляет ScrollViewer данные, необходимые для задания параметров вертикальной полосы прокрутки.

Когда свойству HorizontalScrollBarVisibility элемента ScrollViewer задается значение Visible или Auto, ScrollViewer вызывает метод Measure элемента StackPanel с неограниченной шириной, чтобы определить желаемую ширину панели. ScrollViewer использует эти данные для задания параметров горизонтальной полосы прокрутки. Затем StackPanel передает эту бесконечную ширину в вызовы MeasureOverride его дочерних элементов. Потенциально это может иметь неожиданный эффект для дочерних элементов StackPanel.

Например, если свойству TextWrapping элемента TextBlock задано значение Wrap, в вызове собственного MeasureOverride для определения необходимого количества строк для размещения текста он использует значение availableSize.Width. Но если availableSize.Width имеет неограниченный размер – как это происходит в случае, когда TextBlock размещен в ScrollViewer с активированной горизонтальной полосой прокрутки – у TextBlock нет другого выбора, кроме как возвратить свой размер с текстом без переносов.

Вот почему в приложении TelephonicConversation не следует активировать горизонтальную полосу прокрутки в ScrollViewer.

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

По теме:

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