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

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

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

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

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

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

  • Элементы панели и пользовательские расширения функциональности макета

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

  • Субпиксельная визуализация и округление макета

  • Что дальше?

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

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

На следующем рисунке представлен простой макет.

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

Этот макет можно получить с помощью следующего XAML.

<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 отображается.

Как показано желтым прямоугольником, выделенное пространство для элемента 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();
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ПримечаниеПримечание

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

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

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

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

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

Элементы панели и пользовательские расширения функциональности макета

В WPF имеется группа элементов, производных от Panel. Эти элементы Panel включают многие сложные макеты. Например, вложенные элементы можно легко получить с помощью элемента StackPanel, в то время как более сложные и свободно располагаемые макеты создаются при помощи Canvas.

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

Имя панели

Описание

Canvas

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

DockPanel

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

Grid

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

StackPanel

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

VirtualizingPanel

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

WrapPanel

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

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

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

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

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

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

  • Рекомендуется использовать свойство RenderTransform вместо LayoutTransform везде, где это возможно.

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

  • Избегайте ненужных вызовов UpdateLayout.

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

  • При работе с большой коллекцией Children рекомендуется рассмотреть возможность использования элемента VirtualizingStackPanel вместо обычного StackPanel.

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

Субпиксельная визуализация и округление макета

Графическая система WPF использует аппаратно-независимые модули для поддержки разрешения и независимости от устройства. Каждый независимый от устройства пиксель автоматически масштабируется согласно параметрам системы dots per inch (dpi). Это обеспечивает приложениям WPF корректное масштабирование для различных параметров dpi и автоматически предоставляет приложениям поддержку dpi.

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

По умолчанию округление макета отключено. Чтобы включить округление макета, следует установить свойство UseLayoutRounding в значение true в любом элементе FrameworkElement. Поскольку это свойство зависимостей, значение будет распространено на все дочерние объекты в визуальном дереве. Чтобы включить округление макета для всего пользовательского интерфейса, следует установить свойство UseLayoutRounding в значение true в корневом контейнере. Пример см. в разделе UseLayoutRounding.

Что дальше?

Понимание механизма измерения и упорядочивания элементов – это первый шаг к пониманию макета. Дополнительные сведения о доступных элементах Panel см. в разделе Общие сведения о панелях. Чтобы лучше понять различные свойства размещения, которые могут повлиять на макет, обратитесь к Общие сведения о свойствах Alignment, Margin, Padding. Пример пользовательского элемента Panel см. на странице Пример "Custom Radial Panel" (страница может отображаться на английском языке). Когда все элементы будут готовы к помещению в облегченное приложение, просмотрите раздел Пошаговое руководство. Начало работы с WPF.

См. также

Ссылки

FrameworkElement

UIElement

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

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

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

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