Макет

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

Этот раздел состоит из следующих подразделов.

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

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

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

Screenshot that shows a typical grid, no bounding box superimposed.

Этот макет можно получить с помощью приведенного ниже кода 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.

Screenshot that shows that the TextBlock bounding box is now visible.

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

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

private void getLayoutSlot1(object sender, System.Windows.RoutedEventArgs e)
{
    RectangleGeometry myRectangleGeometry = new RectangleGeometry();
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1);
    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();
}
Private Sub getLayoutSlot1(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim myRectangleGeometry As New RectangleGeometry
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1)
    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

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

В самом простом случае макет является рекурсивной системой, которая изменяет размер и расположение элемента и отображает его на экране. В частности, макет описывает процесс измерения и расположения членов коллекции 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, который передается методу ArrangeOverridePanel в качестве параметра. ArrangeOverride создает finalSize дочернего элемента. Наконец, метод ArrangeCore выполняет окончательную оценку свойств смещения, таких как поле и выравнивание, и помещает дочерний объект в его ячейку макета. Дочерний объект не должен обязательно заполнять все выделенное пространство (и в большинстве случаев не делает этого). Затем управление возвращается родительскому объекту Panel, и обработка макета завершается.

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

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

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

Имя панели Description
Canvas Определяет область, внутри которой можно явным образом разместить дочерние элементы с помощью координат, относящихся к области Canvas.
DockPanel Определяет область, в которой можно горизонтально либо вертикально упорядочивать дочерние элементы относительно друг друга.
Grid Задание области с таблицей переменного размера, состоящей из столбцов и строк.
StackPanel Выравнивает дочерние элементы в одну линию, ориентированную горизонтально или вертикально.
VirtualizingPanel Предоставляет платформу для Panel элементов, которые виртуализации их дочерней коллекции данных. Этот класс является абстрактным.
WrapPanel Размещает дочерние элементы последовательно слева направо, перенося содержимое на следующую строку на краю содержащего поля. Последующее размещение происходит последовательно сверху вниз или справа налево, в зависимости от значения свойства Orientation.

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

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

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

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

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

  • По возможности используйте RenderTransform вместо LayoutTransform.

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

  • Устранение лишних вызовов UpdateLayout.

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

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

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

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

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

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

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

Дальнейшие действия

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

См. также