Поделиться через


Схема

В этом разделе описывается система компоновки 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 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.

Имя панели Описание
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 сохраняет в памяти только те объекты, которые находятся в области видимости родительского элемента. В результате производительность значительно улучшается в большинстве сценариев.

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

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

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

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

Что дальше

Понимание того, как элементы измеряются и упорядочены, является первым шагом в понимании макета. Для получения дополнительной информации о доступных элементах Panel см. обзор панелей . Чтобы лучше понять различные свойства позиционирования, которые могут повлиять на оформление, смотрите обзор выравнивания, полей и заполнения. Когда вы будете готовы объединить все это в легковесное приложение, см. Пошаговое руководство: Мое первое классическое WPF-приложение.

См. также