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

WPF 애플리케이션의 디자인 작업은 레이아웃 계산과 개체 참조의 유효성 검사 과정에서 불필요한 오버헤드를 초래하여 성능에 영향을 줄 수 있습니다. 또한 개체 생성 작업은 특히 런타임에 애플리케이션의 성능 특성에 영향을 줄 수 있습니다.

이 항목에서는 이러한 영역에서 성능과 관련된 권장 사항을 제공합니다.

레이아웃

“레이아웃 단계”라는 용어는 Panel 파생 개체의 자식 컬렉션에 속한 멤버를 측정 및 정렬한 다음, 화면에 그리는 프로세스를 말합니다. 레이아웃 단계는 많은 계산이 요구되는 프로세스이며 컬렉션의 자식 수가 많을수록 더 많은 계산이 필요합니다. 예를 들어, 컬렉션의 자식 UIElement 개체가 해당 위치를 변경할 때마다 레이아웃 시스템에 의한 새 단계가 트리거될 수 있습니다. 개체 특성과 레이아웃 동작 간의 긴밀한 관계로 인해 레이아웃 시스템을 호출할 수 있는 이벤트의 형식을 이해하는 것이 중요합니다. 레이아웃 단계의 불필요한 호출을 되도록 많이 줄이면 애플리케이션의 성능이 향상됩니다.

레이아웃 시스템은 컬렉션의 각 자식 멤버에 대해 두 개의 단계인 측정 단계 및 정렬 단계를 수행합니다. 각 자식 개체는 MeasureArrange 메서드의 구현을 고유하게 재정의하여 고유한 특정 레이아웃 동작을 제공합니다. 가장 간단한 레이아웃은 화면에서 크기 조정, 배치 및 그리기를 수행할 요소가 되는 재귀 시스템입니다.

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

  • Width, Height, Margin 등 크기와 관련된 개체의 FrameworkElement 속성이 평가됩니다.

  • DockPanelDock 속성 또는 StackPanelOrientation 속성과 같은 Panel 관련 논리가 적용됩니다.

  • 모든 자식 개체가 측정된 후 콘텐츠가 정렬 또는 배치됩니다.

  • 자식 개체의 컬렉션이 화면에 그려집니다.

다음 작업 중 하나가 발생할 경우 레이아웃 단계 프로세스가 다시 호출됩니다.

  • 자식 개체가 컬렉션에 추가됩니다.

  • LayoutTransform이 자식 개체에 적용됩니다.

  • UpdateLayout 메서드가 자식 개체에 대해 호출됩니다.

  • 측정 또는 정렬 단계에 영향을 주는 메타데이터로 표시된 종속성 속성의 값에 변경이 발생할 경우

가능한 경우 가장 효율적인 패널 사용

레이아웃 프로세스의 복잡도는 사용하는 Panel 파생 요소의 레이아웃 동작에 따라 직접적으로 달라집니다. 예를 들어, Grid 또는 StackPanel 컨트롤은 Canvas 컨트롤보다 훨씬 더 많은 기능을 제공합니다. 기능이 많이 제공될수록 성능 비용이 증가합니다. 따라서 Grid 컨트롤이 제공하는 기능이 필요하지 않은 경우 Canvas 또는 사용자 지정 패널 등 더 적은 비용이 드는 대체 방법을 사용해야 합니다.

자세한 내용은 패널 개요를 참조하세요.

RenderTransform을 대체하는 대신 업데이트

RenderTransform 속성 값으로 대체하는 대신에 Transform 속성을 업데이트할 수 있습니다. 애니메이션과 관련된 시나리오가 특히 이러한 경우에 해당합니다. 기존 Transform을 업데이트하면 불필요한 레이아웃 계산이 시작되지 않습니다.

하향식 트리 빌드

논리적 트리에서 노드가 추가 또는 제거될 경우 노드의 부모 및 모든 자식에서 속성 무효화가 발생합니다. 결과적으로 이미 유효성이 검사된 노드에서 불필요한 무효화의 비용을 방지하려면 항상 하향식 생성 패턴을 따라야 합니다. 다음 표에서는 각 수준에 단일 TextBlockDockPanel이 있는 150개 수준 깊이의 트리를 하향식 및 상향식으로 빌드할 때의 실행 속도 차이를 보여 줍니다.

동작 트리 빌드(ms) 렌더링—트리 빌드 포함(ms)
상향식 366 454
하향식 11 96

다음 코드 예제에서는 하향식 트리를 만드는 방법을 보여 줍니다.

private void OnBuildTreeTopDown(object sender, RoutedEventArgs e)
{
    TextBlock textBlock = new TextBlock();
    textBlock.Text = "Default";

    DockPanel parentPanel = new DockPanel();
    DockPanel childPanel;

    myCanvas.Children.Add(parentPanel);
    myCanvas.Children.Add(textBlock);

    for (int i = 0; i < 150; i++)
    {
        textBlock = new TextBlock();
        textBlock.Text = "Default";
        parentPanel.Children.Add(textBlock);

        childPanel = new DockPanel();
        parentPanel.Children.Add(childPanel);
        parentPanel = childPanel;
    }
}
Private Sub OnBuildTreeTopDown(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim textBlock As New TextBlock()
    textBlock.Text = "Default"

    Dim parentPanel As New DockPanel()
    Dim childPanel As DockPanel

    myCanvas.Children.Add(parentPanel)
    myCanvas.Children.Add(textBlock)

    For i As Integer = 0 To 149
        textBlock = New TextBlock()
        textBlock.Text = "Default"
        parentPanel.Children.Add(textBlock)

        childPanel = New DockPanel()
        parentPanel.Children.Add(childPanel)
        parentPanel = childPanel
    Next i
End Sub

논리적 트리에 대한 자세한 내용은 WPF의 트리를 참조하세요.

참고 항목