Система макета

Обновлен: Ноябрь 2007

В этом разделе описывается система макета Windows Presentation Foundation (WPF). Понимание того, как и когда происходят вычисления макета, важно для создания привлекательных и высокопроизводительных пользовательских интерфейсов.

В этом разделе содержатся следующие подразделы.

  • Система макета

  • Ограничивающие поля элемента

  • Измерение и расположение дочерних элементов

  • Элементы панели и пользовательские поведения макета

  • Вопросы производительности макета

  • Что дальше?

  • См. также

Система макета

Термин «макет» описывает процесс измерения и упорядочивания членов коллекции Children элемента Panel и их дальнейшего отображения на экране. Это трудоемкий процесс, и с увеличением размера коллекции Children увеличивается и объем вычислений. Сложность может также быть вызвана поведением макета, определяемого элементом Panel, которому принадлежит коллекция. Относительно простой макет, например Canvas, поможет создать отличную производительность в случае, когда не требуется более сложный Panel, такой как Grid.

Каждый раз, когда дочерний UIElement изменяет свое положение, он потенциально имеет возможность запустить новый проход при помощи системы макета. Таким образом, важно понимать события, которые может вызвать система макета, так как ненужный вызов может привести к ухудшению производительности приложения.

В самом простом случае макет является рекурсивной системой, в процессе которой программа изменяет размер и расположение элемента и отображает его на экране. Система макета совершает два прохода для каждого члена коллекции Children, проход измерения и проход компоновки. Каждый дочерний Panel предоставляет свои собственные методы MeasureOverride и ArrangeOverride, чтобы добиться собственного определенного поведения макета. Это ряд событий, возникающий, когда вызывается система макета.

  1. Дочерний UIElement начинает процесс макета по первому измерению его основных свойств.

  2. Вычисляются размерные свойства, определенные на FrameworkElement. Такими свойствами являются Width, Height и Margin.

  3. Применяется специальная логика Panel, а именно направление Dock или наложение Orientation.

  4. Содержимое располагается после того, как все потомки были измерены.

  5. Коллекция Children отображается на экране.

  6. Процесс вызывается снова, если в коллекцию добавляются дополнительные Children, применяется LayoutTransform или вызывается метод UpdateLayout.

Этот процесс и средства, при помощи которых он вызывается, более подробно рассматриваются в следующих подразделах.

Ограничивающие поля элемента

При планировании макета приложения Windows Presentation Foundation (WPF) важно понимать принцип функционирования ограничивающего поля, которое окружает все элементы. Следующий пример поможет вам разобраться в поведения системы макета. Каждый FrameworkElement, обрабатываемый макетом системы, может быть рассмотрен как прямоугольник, который расположен в разделе макета. Программа предоставляет класс LayoutInformation, который может возвращать геометрические границы выделения макета элемента или ячейки. Размер этого прямоугольника определяется системой путем вычисления доступного пространства экрана, размера любых ограничений, конкретных свойств макета, например поля и заполнения, а также индивидуального поведения родительского элемента Panel. Обрабатывая эти данные, система имеет возможность вычислить положение всех потомков заданного Panel. Важно помнить, что изменение размеров характеристик, определенных для родительского элемента (например Border), влияет на его дочерние элементы.

В качестве примера рассмотрим следующий сценарий простого макета.

Обычная сетка, без обрамляющего прямоугольника.

Этот макет можно получить с помощью следующих Язык XAML (Extensible Application Markup Language).

<Grid Name="myGrid" Background="LightSteelBlue" Height="150">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="250"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <TextBlock Name="txt1" Margin="5" FontSize="16" FontFamily="Verdana" Grid.Column="0" Grid.Row="0">Hello World!</TextBlock>
  <Button Click="getLayoutSlot1" Width="125" Height="25" Grid.Column="0" Grid.Row="1">Show Bounding Box</Button>
  <TextBlock Name="txt2" Grid.Column="1" Grid.Row="2"/>
</Grid>

Отдельный элемент TextBlock помещен внутри Grid. Текст заполняет только верхний левый угол столбца, в котором он был помещен, а выделенное для TextBlock место на самом деле намного больше. Ограничивающее поле любого FrameworkElement может быть получено с помощью метода GetLayoutSlot. С помощью этого метода ограничивающее поле элемента TextBlock становится наложенным (это возможно, так как TextBlock размещается внутри Grid элемента Panel, который позволяет совместное использование координат макета).

Теперь обрамляющий прямоугольник для TextBlock отображается.

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

Ячейка макета TextBlock возвращается и преобразуется в Path с помощью метода GetLayoutSlot. Эта технология может быть полезна для отображения ограничивающего поля элемента.

Private Sub getLayoutSlot1(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim myRectangleGeometry As New RectangleGeometry
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1)
    Dim myGeometryDrawing As New GeometryDrawing
    Dim myPath As New Path
    myPath.Data = myRectangleGeometry
    myPath.Stroke = Brushes.LightGoldenrodYellow
    myPath.StrokeThickness = 5
    Grid.SetColumn(myPath, 0)
    Grid.SetRow(myPath, 0)
    myGrid.Children.Add(myPath)
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString()
End Sub
private void getLayoutSlot1(object sender, System.Windows.RoutedEventArgs e)
{
    RectangleGeometry myRectangleGeometry = new RectangleGeometry();
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1);
    GeometryDrawing myGeometryDrawing = new GeometryDrawing();
    Path myPath = new Path();
    myPath.Data = myRectangleGeometry;
    myPath.Stroke = Brushes.LightGoldenrodYellow;
    myPath.StrokeThickness = 5;
    Grid.SetColumn(myPath, 0);
    Grid.SetRow(myPath, 0);
    myGrid.Children.Add(myPath);
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString();
}

Измерение и расположение дочерних элементов

При отображении содержимого объекта Window автоматически вызывается система макета. Чтобы отобразить содержимое, Content окна должен определить корневой Panel, который служит для определения структуры, при помощи которой Children располагаются на экране. См. подраздел Элементы панели и пользовательские поведения макета для получения списка доступных элементов Panel и сведений о создании пользовательских элементов макета.

Первый проход макета является проходом измерения, во время которого вычисляется каждый член коллекции Children. Процесс начинается с вызова метода Measure. Этот метод вызывается в реализации родительского элемента Panel и не должен вызываться явным образом для выполнения макета.

Во-первых, вычисляются собственные свойства размера UIElement, такие как Clip и Visibility. Это создает значение с именем constraintSize, которое передается MeasureCore.

Затем обрабатываются свойства структуры, определенные на FrameworkElement. Это влияет на значение constraintSize. Эти свойства часто описывают размерные характеристики базового UIElement, например его Height, Width, Margin и Style. Каждое из этих свойств может изменить пространство, необходимое для отображения элемента. Затем вызывается MeasureOverride с constraintSize в качестве параметра.

ms745058.alert_note(ru-ru,VS.90).gifПримечание.

Свойства Height, Width, ActualHeight и ActualWidth различаются. Например, свойство ActualHeight является значением, вычисляемым на основе других вводимых высот и системы макета. Значение задается самой системой макета в зависимости от фактического прохода отрисовки, а поэтому может немного отставать от значения атрибутов некоторых свойств, которые являются основой для изменения ввода, например Height.

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

Конечной целью прохода измерения для потомка является определение его DesiredSize, которое происходит во время вызова MeasureCore. Это значение хранится Measure для использования во время процесса компоновки содержимого.

Процесс компоновки начинается с вызова метода Arrange. Во время прохода компоновки родительский элемент Panel создает прямоугольник, представляющий границы потомка. Это значение передается методу ArrangeCore для обработки.

Метод ArrangeCore вычисляет DesiredSize потомка и оценивает все дополнительные поля, которые могут повлиять на отображаемый размер элемента. Кроме того, метод создает arrangeSize, который передается ArrangeOverride панели в качестве параметра. ArrangeOverride создает finalSize потомка, а метод ArrangeCore затем делает последнюю оценку свойств смещения — полей и выравнивания — и помещает потомка в его ячейку макета. Дочерний элемент не должен обязательно заполнять все выделенное пространство (и в большинстве случаев не будет делать этого). Затем элемент управления возвращается к родительскому элементу Panel, и обработка макета завершается.

Элементы панели и пользовательские поведения макета

Windows Presentation Foundation (WPF) включает производный набор элементов Panel, которые позволяют создавать различные сложные макеты. Общие сценарии (например, наложение элементов) можно легко получить с помощью элемента StackPanel, в то время как более сложные и свободно располагаемые макеты создаются при помощи Canvas.

В следующей таблице перечислены доступные элементы макета.

Имя панели

Описание

Canvas

Определяет область, внутри которой можно явным образом разместить дочерние элементы при помощи относительных координат в области Canvas.

DockPanel

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

Grid

Определяет гибкую область сетки, состоящей из строк и столбцов.

StackPanel

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

VirtualizingPanel

Предоставляет структуру для элементов Panel, которые «виртуализируют» свою дочернюю коллекцию данных. Этот класс является абстрактным.

WrapPanel

Размещает дочерние элементы в последовательности слева направо, перенося содержимое на следующую строку на границе содержащего поля. Дельнейшее упорядочивание происходит последовательно сверху вниз или справа налево в зависимости от значения свойства Orientation.

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

Для сценариев, требующих такие макеты приложения, которые невозможно создать при помощи любого из предопределенных элементов Panel, пользовательские поведения макета можно получить путем наследования из Panel и переопределения методов MeasureOverride и ArrangeOverride. Пример см. в разделе Пример пользовательской радиальной панели.

Вопросы производительности макета

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

Свойства зависимости, значения которых могут привести к инициализации системы макета, помечаются открытыми флагами. AffectsMeasure и AffectsArrange предоставляют полезные указания о том, какие изменения значения свойства вызовут рекурсивное обновление системы макета. В общем случае любое свойство, которое может повлиять на размер окна ограничивающего элемента, должно установить флаг AffectsMeasure равным true. Дополнительные сведения см. в разделе Общие сведения о свойствах зависимости.

LayoutTransform — это удобный способ определения содержимого пользовательский интерфейс. Однако, если результату преобразования не требуется влиять на положение других элементов, вместо этого следует использовать RenderTransform, так как RenderTransform не вызывает систему макета. LayoutTransform применяет преобразование и вызывает рекурсивное обновление макета для расчета нового положения затронутого элемента.

Избегайте ненужных вызовов UpdateLayout. Этот метод вызывает рекурсивное обновление макета, что зачастую не требуется. Предоставьте вызов этого метода системе макета, если вы не уверены в необходимости полного обновления.

При работе с большими коллекциями Children, рассмотрите возможность использования VirtualizingStackPanel вместо обычного StackPanel. «Виртуализация» дочерней коллекции приводит к тому, что VirtualizingStackPanel сохраняет в памяти только те объекты, которые в настоящее время находятся внутри родительского ViewPort. В результате этого производительность значительно увеличивается в большинстве сценариев.

Что дальше?

Понимание механизма измерения и упорядочивания элементов — это первый шаг к пониманию макета как системы. Для более подробного описания доступных элементов Panel см. раздел Общие сведения о панелях. Чтобы лучше понять различные свойства размещения, которые могут повлиять на макет, обратитесь к Общие сведения о свойствах Alignment, Margin, Padding. Пример пользовательского элемента панели содержится в разделе Пример пользовательской радиальной панели. Когда все элементы будут готовы к помещению в облегченное приложение, просмотрите раздел Введение в Windows Presentation Foundation.

См. также

Основные понятия

Общие сведения о панелях

Общие сведения о свойствах Alignment, Margin, Padding

Привязка точек в приложениях WPF

XAMLPad

Оптимизация производительности: разметка и разработка

Ссылки

FrameworkElement

UIElement