레이아웃 시스템

업데이트: 2007년 11월

이 항목에서는 WPF(Windows Presentation Foundation) 레이아웃 시스템에 대해 설명합니다. 세련되고 성능이 좋은 사용자 인터페이스를 제작하려면 언제 어떻게 레이아웃을 계산해야 하는지를 이해해야 합니다.

이 항목에는 다음 단원이 포함되어 있습니다.

  • 레이아웃 시스템

  • 요소 경계 상자

  • 자식 항목 측정 및 정렬

  • 패널 요소 및 사용자 지정 레이아웃 동작

  • 레이아웃 성능 고려 사항

  • 새로운 기능

  • 관련 항목

레이아웃 시스템

"레이아웃"이라는 용어는 Panel 요소의 Children 컬렉션에 속한 멤버를 측정 및 정렬한 다음 화면에 그리는 프로세스를 말합니다. 이는 집약적인 프로세스로서 Children 컬렉션이 클수록 계산 수도 많습니다. 컬렉션을 소유하는 Panel 요소가 정의하는 레이아웃 동작을 기준으로 보다 복잡한 레이아웃 계산도 수행할 수 있습니다. Canvas와 같이 상대적으로 간단한 레이아웃은 Grid와 같은 보다 복잡한 Panel이 필요하지 않을 때 뛰어난 성능을 제공합니다.

자식 UIElement가 해당 위치를 변경할 때마다 레이아웃 시스템에 의한 새 처리 단계가 트리거될 수 있습니다. 따라서 레이아웃 시스템을 호출할 수 있는 이벤트를 이해하는 것이 중요합니다. 불필요한 호출로 인해 응용 프로그램 성능이 저하될 수 있기 때문입니다.

가장 간단한 레이아웃은 화면에서 요소의 크기 조정, 배치 및 그리기를 수행하는 재귀 시스템입니다. 레이아웃 시스템은 Children 컬렉션의 각 멤버에 대해 두 처리 단계인 측정 처리 단계 및 정렬 처리 단계를 수행합니다. 각 자식 Panel은 고유한 특정 레이아웃 동작을 제공하기 위해 고유의 MeasureOverrideArrangeOverride 메서드를 제공합니다. 이는 레이아웃 시스템이 호출될 때마다 발생하는 일련의 이벤트입니다.

  1. 자식 UIElement는 먼저 핵심 속성이 측정되도록 하여 레이아웃 프로세스를 시작합니다.

  2. Width, HeightMargin과 같이 FrameworkElement에 정의된 크기 조정 속성이 평가됩니다.

  3. Dock 방향 또는 스택 Orientation과 같은 Panel 고유 논리가 적용됩니다.

  4. 모든 자식 항목을 측정한 후에 내용이 정렬됩니다.

  5. Children 컬렉션이 화면에 그려집니다.

  6. Children이 화면에 더 추가되거나 LayoutTransform이 적용되거나 UpdateLayout 메서드가 호출되면 프로세스가 다시 호출됩니다.

이 프로세스 및 이 프로세스를 호출하는 방법은 이후 단원에서 자세히 정의되어 있습니다.

요소 경계 상자

WPF(Windows Presentation Foundation)에서 응용 프로그램 레이아웃을 고려할 때는 모든 요소를 둘러싸고 있는 경계 상자를 이해하는 것이 중요합니다. 이러한 추상화는 레이아웃 시스템의 동작을 파악하는 데 도움이 됩니다. 레이아웃 시스템에서 사용하는 각 FrameworkElement를 레이아웃 위치에 배치되는 직사각형으로 생각할 수 있습니다. 요소의 레이아웃 할당 또는 슬롯에 대한 기하학적 경계를 반환할 수 있는 클래스 LayoutInformation이 노출됩니다. 해당 직사각형의 크기는 시스템에서 사용 가능한 화면 크기, 제약 조건의 크기, 레이아웃 고유 속성(예: 여백 및 안쪽 여백) 그리고 부모 Panel 요소의 개별 동작을 계산하여 결정합니다. 이 데이터를 처리함으로써 시스템은 특정 Panel의 모든 자식 항목의 위치를 계산할 수 있습니다. Border와 같이 부모 요소에 정의된 크기 조정 특성은 자식 항목에 영향을 줍니다.

예를 들어 다음의 간단한 레이아웃 시나리오를 생각해 보십시오.

경계 상자가 없는 일반적인 Grid

이 레이아웃은 다음 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 요소의 경계 상자를 덧붙입니다. 이는 TextBlockGrid, 레이아웃 좌표의 공유를 허용하는 Panel 요소 내에서 호스팅되기 때문에 가능합니다.

이제 TextBlock의 경계 상자가 표시됨

주위를 둘러싸는 흰색 선을 통해 알 수 있듯이 TextBlock 요소에 할당된 부분은 채운 공간보다 실제로 훨씬 큽니다. 요소가 Grid에 더 추가될 때 추가되는 요소의 형식과 크기에 따라 이 할당이 축소되거나 확장될 수 있습니다.

요소의 경계 상자를 표시하는 데 유용할 수 있는 기술인 GetLayoutSlot 메서드를 사용하여 TextBlock의 레이아웃 슬롯이 반환되고 Path로 변환됩니다.

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 요소의 구현 내에서 호출되며 레이아웃이 발생하기 위해 명시적으로 호출될 필요는 없습니다.

첫째, ClipVisibility 같은 UIElement의 기본 크기 속성이 평가됩니다. 이때 constraintSize라는 값이 생성되어 MeasureCore로 전달됩니다.

둘째, FrameworkElement에 정의된 프레임워크 속성이 처리되어 constraintSize의 값에 영향을 줍니다. 이 속성은 Height, Width, MarginStyle 같은 내부 UIElement의 크기 조정 특성을 설명하는 경향이 있습니다. 이러한 각각의 속성은 요소를 표시하는 데 필요한 공간을 변경할 수 있습니다. 이 경우 매개 변수인 constraintSize를 사용하여 MeasureOverride가 호출됩니다.

참고

Height 속성과 Width 속성 사이에 그리고 ActualHeight 속성과 ActualWidth 속성 사이에 차이점이 있습니다. 예를 들어 ActualHeight 속성은 다른 높이 입력 및 레이아웃 시스템에 따라 계산된 값입니다. 이 값은 실제 렌더링 처리 단계에 따라 레이아웃 시스템 자체적으로 설정되므로 입력 변경의 기준인 Height 같은 속성의 설정 값 뒤에 약간 지연될 수 있습니다.

ActualHeight는 계산된 값이므로 레이아웃 시스템에서 수행되는 다양한 작업의 결과로 여러 차례 또는 점증적으로 변경될 수 있습니다. 레이아웃 시스템은 자식 요소에 필요한 측정 공간, 부모 요소에 의한 제약 조건 등을 계산할 수도 있습니다.

측정 처리 단계의 궁극적인 목표는 MeasureCore 호출 동안 발생하는 DesiredSize를 자식 항목이 확인할 수 있도록 하는 것입니다. 이 값은 내용 정렬 프로세스에서 사용할 수 있도록 Measure에 의해 저장됩니다.

정렬 프로세스는 Arrange 메서드에 대한 호출로 시작됩니다. 정렬 처리 단계 동안 부모 Panel 요소는 자식 항목의 경계를 나타내는 직사각형을 생성합니다. 이 값은 처리를 위해 ArrangeCore 메서드에 전달됩니다.

ArrangeCore 메서드는 자식 항목의 DesiredSize를 평가하고, 요소의 렌더링된 크기에 영향을 줄 수 있는 추가적인 여백을 평가하고, 패널의 ArrangeOverride에 매개 변수로 전달되는 arrangeSize를 생성합니다. ArrangeOverride는 자식 항목의 finalSize를 생성하고, 마지막으로 ArrangeCore 메서드는 여백 및 맞춤 같은 오프셋 속성을 최종 평가하고 레이아웃 슬롯 안에 자식 항목을 배치합니다. 자식 항목은 할당된 공간을 모두 채울 필요가 없으며 실제로도 모두 채우는 경우가 드뭅니다. 그런 다음 컨트롤이 부모 Panel에 반환되고 레이아웃 프로세스가 완료됩니다.

패널 요소 및 사용자 지정 레이아웃 동작

WPF(Windows Presentation Foundation)에는 다수의 복잡한 레이아웃을 가능하게 하는 파생된 Panel 요소 집합이 있습니다. 요소 스택과 같은 일반적인 시나리오는 StackPanel 요소를 사용하여 쉽게 달성할 수 있는 반면 보다 복잡하고 흐름이 자유로운 레이아웃은 Canvas를 사용하여 달성할 수 있습니다.

다음 표에서는 사용 가능한 레이아웃 요소를 요약하여 설명합니다.

패널 이름

설명

Canvas

Canvas 영역에 상대적인 좌표를 사용하여 자식 요소를 명시적으로 배치할 수 있는 영역을 정의합니다.

DockPanel

자식 요소를 서로를 기준으로 가로 또는 세로로 정렬할 수 있는 영역을 정의합니다.

Grid

열과 행으로 구성되는 유연한 표 영역을 정의합니다.

StackPanel

가로 또는 세로 방향으로 지정할 수 있는 단일 선에 따라 자식 요소를 정렬합니다.

VirtualizingPanel

자식 데이터 컬렉션을 "가상화"하는 Panel 요소를 위한 프레임워크를 제공합니다. 추상 클래스입니다.

WrapPanel

자식 요소를 왼쪽에서 오른쪽으로 순차적으로 배치하고, 포함하는 상자의 가장자리에서 내용을 다음 줄로 바꿉니다. 이후에는 Orientation 속성 값에 따라 순서가 위에서 아래로 또는 오른쪽에서 왼쪽으로 순차적으로 지정됩니다.

각 요소의 사용 방법을 보여 주는 코드 샘플은 레이아웃 샘플을 참조하십시오.

미리 정의된 Panel 요소를 사용하여 달성할 수 없는 응용 프로그램 레이아웃이 필요한 시나리오의 경우에는 Panel로부터 상속하고 MeasureOverrideArrangeOverride 메서드를 재정의함으로써 사용자 지정 레이아웃 동작을 완성할 수 있습니다. 예제를 보려면 사용자 지정 방사형 패널 샘플을 참조하십시오.

레이아웃 성능 고려 사항

레이아웃은 재귀적인 프로세스입니다. Children 컬렉션에 있는 각 자식 요소는 시스템이 호출될 때마다 처리됩니다. 따라서 필요하지 않을 때 시스템을 트리거하는 것을 피해야 합니다. 다음 팁은 보다 뛰어난 성능을 얻는 데 도움이 됩니다.

레이아웃 시스템을 초기화할 수 있는 값을 가지고 있는 종속성 속성은 공용 플래그로 표시되어 있습니다. AffectsMeasureAffectsArrange는 어떤 속성 값 변경이 레이아웃 시스템의 재귀적 업데이트를 강제하는지에 대한 유용한 단서를 제공합니다. 일반적으로 요소의 경계 상자 크기에 영향을 줄 수 있는 속성은 AffectsMeasure 플래그를 true로 설정해야 합니다. 자세한 내용은 종속성 속성 개요을 참조하십시오.

LayoutTransform은 UI(사용자 인터페이스)의 내용에 영향을 주는 매우 유용한 방법이 될 수 있습니다. 그러나 변환 효과가 다른 요소의 위치에 영향을 줄 필요가 없는 경우에는 RenderTransform을 대신 사용하는 것이 좋습니다. RenderTransform은 레이아웃 시스템을 호출하지 않기 때문입니다. LayoutTransform은 변환을 적용하고 재귀적 레이아웃 업데이트를 강제하여 영향을 받는 요소의 새 위치를 계산합니다.

UpdateLayout에 대한 불필요한 호출을 피하십시오. 이 메서드는 재귀적 레이아웃 업데이트를 강제하며 불필요한 경우가 많습니다. 전체 업데이트가 필요하다고 확신하는 경우를 제외하고는 레이아웃 시스템에서 이 메서드를 대신 호출하도록 하십시오.

Children 컬렉션을 처리할 때는 일반 StackPanel 대신 VirtualizingStackPanel을 사용해 보십시오. 자식 컬렉션을 "가상화"함으로써 VirtualizingStackPanel은 현재 부모의 ViewPort 내에 있는 메모리의 개체만 유지합니다. 따라서 대부분의 시나리오에서 성능이 크게 향상됩니다.

추가 사항

요소의 측정 방법과 정렬 방법을 이해하는 것이 레이아웃을 하나의 시스템으로 이해하기 위한 첫 단계입니다. 사용 가능한 Panel 요소에 대한 자세한 내용은 Panel 개요를 참조하십시오. 레이아웃에 영향을 줄 수 있는 다양한 배치 속성에 대한 자세한 내용은 맞춤, 여백 및 안쪽 여백 개요를 참조하십시오. 사용자 지정 패널 요소의 예제는 사용자 지정 방사형 패널 샘플을 참조하십시오. 이 모든 것을 간단한 응용 프로그램 내에 구현할 준비가 되었으면 Windows Presentation Foundation 시작을 참조하십시오.

참고 항목

개념

Panel 개요

맞춤, 여백 및 안쪽 여백 개요

WPF 응용 프로그램에서 픽셀 맞추기

XAMLPad

성능 최적화: 레이아웃 및 디자인

참조

FrameworkElement

UIElement