성능 최적화: 텍스트

WPF에서는 다양한 기능의 UI(사용자 인터페이스) 컨트롤을 사용하여 텍스트 콘텐츠를 프레젠테이션할 수 있습니다. 일반적으로 세 가지 계층으로 텍스트 렌더링을 나눌 수 있습니다.

  1. GlyphsGlyphRun 개체를 직접 사용.

  2. FormattedText 개체 사용.

  3. 상위 수준 컨트롤(예: TextBlockFlowDocument 개체) 사용.

이 항목에서는 텍스트 렌더링 성능 권장 사항을 제공합니다.

문자 모양 수준에서 텍스트 렌더링

WPF(Windows Presentation Foundation)는 텍스트 서식 지정 후 가로채 유지하려는 고객을 위해 Glyphs에 직접 액세스할 수 있는 문자 모양 수준 태그를 포함한 고급 텍스트 지원을 제공합니다. 이러한 기능을 통해 다음과 같은 각 시나리오의 다양한 텍스트 렌더링 요구 사항을 충족시킬 수 있습니다.

  • 고정된 형식 문서의 화면 표시

  • 인쇄 시나리오

    • XAML(Extensible Application Markup Language)을 디바이스 프린터 언어로 사용.

    • Microsoft XPS Document Writer.

    • 이전 프린터 드라이버, Win32 애플리케이션에서 고정된 형식으로 출력.

    • 인쇄 스풀 형식

  • 이전 버전의 Windows 및 기타 컴퓨팅 디바이스에 대한 클라이언트를 포함하는 고정된 형식 문서 표시.

참고

GlyphsGlyphRun은 고정 형식 문서 프레젠테이션 및 인쇄 시나리오용으로 디자인되었습니다. WPF는 LabelTextBlock같은 일반 레이아웃 및 UI(사용자 인터페이스) 시나리오에 대한 몇 가지 요소를 제공합니다. 레이아웃 및 UI 시나리오에 대한 자세한 내용은 WPF의 입력 체계를 참조하세요.

다음 예제에서는 XAML에서 Glyphs 개체의 속성을 정의하는 방법을 보여 줍니다. 이 예제에서는 로컬 컴퓨터의 C:\WINDOWS\Fonts 폴더에 Arial, Courier New 및 Times New Roman 글꼴이 설치되어 있다고 가정합니다.

<!-- The example shows how to use a Glyphs object. -->
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >

   <StackPanel Background="PowderBlue">

      <Glyphs
         FontUri             = "C:\WINDOWS\Fonts\TIMES.TTF"
         FontRenderingEmSize = "100"
         StyleSimulations    = "BoldSimulation"
         UnicodeString       = "Hello World!"
         Fill                = "Black"
         OriginX             = "100"
         OriginY             = "200"
      />

   </StackPanel>
</Page>

DrawGlyphRun 사용

사용자 지정 컨트롤이 있고 문자 모양을 렌더링하려는 경우 DrawGlyphRun 메서드를 사용합니다.

WPF는 또한 FormattedText 개체를 사용하여 사용자 지정 텍스트 서식 지정을 위한 하위 수준 서비스를 제공합니다. WPF(Windows Presentation Foundation)에서 텍스트를 렌더링하는 가장 효율적인 방법은 GlyphsGlyphRun을 사용하여 문자 모양 수준에서 텍스트 콘텐츠를 생성하는 것입니다. 그러나 이 경우 TextBlock, FlowDocument 등 WPF(Windows Presentation Foundation) 컨트롤의 기본 제공 기능인 사용하기 쉬운 서식 있는 텍스트 서식 지정이 손실됩니다.

FormattedText 개체

FormattedText 개체를 사용하면 여러 줄 텍스트를 그릴 수 있으며 이 텍스트에 있는 각 문자의 서식은 개별적으로 지정할 수 있습니다. 자세한 내용은 서식 있는 텍스트 그리기를 참조하세요.

서식이 지정된 텍스트를 만들려면 FormattedText 생성자를 호출하여 FormattedText 개체를 만듭니다. 첫 서식 있는 텍스트 문자열을 만든 다음 다양한 서식 지정 스타일을 적용할 수 있습니다. 애플리케이션에서 고유한 레이아웃을 구현하려는 경우 TextBlock 등의 컨트롤 사용보다는 FormattedText가 더 좋은 선택입니다. FormattedText 개체에 관한 자세한 내용은 서식 있는 텍스트 그리기를 참조하세요.

FormattedText 개체는 하위 수준 텍스트 서식 지정 기능을 제공합니다. 하나 이상의 문자에 여러 서식 지정 스타일을 적용할 수 있습니다. 예를 들어, SetFontSizeSetForegroundBrush 메서드를 둘 다 호출하여 텍스트에서 처음 5개 문자의 서식 지정을 변경할 수 있습니다.

다음 코드 예제에서는 FormattedText 개체를 만들고 렌더링합니다.

protected override void OnRender(DrawingContext drawingContext)
{
    string testString = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor";

    // Create the initial formatted text string.
    FormattedText formattedText = new FormattedText(
        testString,
        CultureInfo.GetCultureInfo("en-us"),
        FlowDirection.LeftToRight,
        new Typeface("Verdana"),
        32,
        Brushes.Black);

    // Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears.
    formattedText.MaxTextWidth = 300;
    formattedText.MaxTextHeight = 240;

    // Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters.
    // The font size is calculated in terms of points -- not as device-independent pixels.
    formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5);

    // Use a Bold font weight beginning at the 6th character and continuing for 11 characters.
    formattedText.SetFontWeight(FontWeights.Bold, 6, 11);

    // Use a linear gradient brush beginning at the 6th character and continuing for 11 characters.
    formattedText.SetForegroundBrush(
                            new LinearGradientBrush(
                            Colors.Orange,
                            Colors.Teal,
                            90.0),
                            6, 11);

    // Use an Italic font style beginning at the 28th character and continuing for 28 characters.
    formattedText.SetFontStyle(FontStyles.Italic, 28, 28);

    // Draw the formatted text string to the DrawingContext of the control.
    drawingContext.DrawText(formattedText, new Point(10, 0));
}
Protected Overrides Sub OnRender(ByVal drawingContext As DrawingContext)
    Dim testString As String = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"

    ' Create the initial formatted text string.
    Dim formattedText As New FormattedText(testString, CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, New Typeface("Verdana"), 32, Brushes.Black)

    ' Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears.
    formattedText.MaxTextWidth = 300
    formattedText.MaxTextHeight = 240

    ' Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters.
    ' The font size is calculated in terms of points -- not as device-independent pixels.
    formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5)

    ' Use a Bold font weight beginning at the 6th character and continuing for 11 characters.
    formattedText.SetFontWeight(FontWeights.Bold, 6, 11)

    ' Use a linear gradient brush beginning at the 6th character and continuing for 11 characters.
    formattedText.SetForegroundBrush(New LinearGradientBrush(Colors.Orange, Colors.Teal, 90.0), 6, 11)

    ' Use an Italic font style beginning at the 28th character and continuing for 28 characters.
    formattedText.SetFontStyle(FontStyles.Italic, 28, 28)

    ' Draw the formatted text string to the DrawingContext of the control.
    drawingContext.DrawText(formattedText, New Point(10, 0))
End Sub

FlowDocument, TextBlock 및 Label 컨트롤

WPF에는 화면에 텍스트를 그리는 데 사용하는 여러 컨트롤이 포함됩니다. 각 컨트롤은 다른 시나리오를 대상으로 하며 고유 기능 및 제한 사항 목록을 가지고 있습니다.

FlowDocument가 TextBlock 또는 Label 이외의 성능에 미치는 영향

일반적으로 TextBlock 요소는 UI(사용자 인터페이스)의 간략한 문장과 같이 제한된 텍스트 지원이 필요할 때 사용해야 합니다. Label은 최소 텍스트 지원이 필요한 경우 사용할 수 있습니다. FlowDocument 요소는 콘텐츠의 풍부한 프레젠테이션을 지원하는 재배치 문서용 컨테이너이므로 TextBlock 또는 Label 컨트롤을 사용하는 것보다 성능에 더 많은 영향을 줍니다.

FlowDocument에 관한 자세한 내용은 유동 문서 개요를 참조하세요.

FlowDocument에서 TextBlock 사용 안 함

TextBlock 요소는 UIElement에서 파생됩니다. Run 요소는 TextElement에서 파생되며 UIElement 파생 개체보다 사용 비용이 더 적습니다. FlowDocument에서 텍스트 콘텐츠를 표시하는 데는 가능한 경우 TextBlock보다 Run을 사용합니다.

다음 태그 샘플은 FlowDocument 내에 텍스트 콘텐츠를 설정하는 두 가지 방법을 보여 줍니다.

<FlowDocument>

  <!-- Text content within a Run (more efficient). -->
  <Paragraph>
    <Run>Line one</Run>
  </Paragraph>

  <!-- Text content within a TextBlock (less efficient). -->
  <Paragraph>
    <TextBlock>Line two</TextBlock>
  </Paragraph>

</FlowDocument>

텍스트 속성을 설정하여 실행 사용 방지

일반적으로 TextBlock 내에서 Run을 사용하는 것은 명시적 Run 개체를 사용하지 않는 것보다 더 성능 집약적입니다. 텍스트 속성을 설정하기 위해 Run을 사용하는 경우에는 대신 TextBlock에서 직접 해당 속성을 설정합니다.

다음 태그 샘플에서는 텍스트 속성(이 경우 FontWeight 속성)을 설정하는 두 가지 방법을 보여 줍니다.

<!-- Run is used to set text properties. -->
<TextBlock>
  <Run FontWeight="Bold">Hello, world</Run>
</TextBlock>

<!-- TextBlock is used to set text properties, which is more efficient. -->
<TextBlock FontWeight="Bold">
  Hello, world
</TextBlock>

다음 표에서는 명시적 Run을 사용하거나 사용하지 않고 1000개 TextBlock 개체를 표시하는 비용을 보여 줍니다.

TextBlock 형식 만든 시간(ms) 렌더링 시간(ms)
Run 설정 텍스트 속성 146 540
TextBlock 설정 텍스트 속성 43 453

Label.Content 속성에 데이터 바인딩 방지

String 원본에서 자주 업데이트되는 Label 개체가 있는 시나리오를 살펴봅니다. Label 요소의 Content 속성을 String 원본 개체에 데이터 바인딩하는 경우 성능이 저하될 수 있습니다. 원본 String이 사용될 때마다 이전 String 개체는 무시되고 새로운 String이 다시 만들어집니다. String 개체는 변경 불가능하므로 수정할 수 없습니다. 따라서 이로 인해 Label 개체의 ContentPresenter가 이전 콘텐츠를 무시하고 새 콘텐츠를 다시 만들어 새 String을 표시합니다.

이 문제에 대한 해결책은 간단합니다. Label이 사용자 지정 ContentTemplate 값으로 설정되지 않은 경우 LabelTextBlock으로 바꾸고 해당 Text 속성을 원본 문자열에 데이터 바인딩합니다.

데이터 바인딩된 속성 업데이트 시간(ms)
Label.Content 835
TextBlock.Text 242

Hyperlink 개체는 흐름 콘텐츠 내에서 하이퍼링크를 호스트할 수 있는 인라인 수준의 흐름 콘텐츠 요소입니다.

여러 Hyperlink 요소를 동일한 TextBlock 내에서 함께 그룹화하여 해당 요소 사용을 최적화할 수 있습니다. 이렇게 하면 애플리케이션에서 만드는 개체의 수를 최소화할 수 있습니다. 예를 들어 다음과 같은 여러 개의 하이퍼링크를 표시할 수 있습니다.

MSN 홈 | 내 MSN

다음 태그 예제에서는 하이퍼링크를 표시하는 데 사용되는 여러 TextBlock 요소를 보여 줍니다.

<!-- Hyperlinks in separate TextBlocks. -->
<TextBlock>
  <Hyperlink TextDecorations="None" NavigateUri="http://www.msn.com">MSN Home</Hyperlink>
</TextBlock>

<TextBlock Text=" | "/>

<TextBlock>
  <Hyperlink TextDecorations="None" NavigateUri="http://my.msn.com">My MSN</Hyperlink>
</TextBlock>

다음 태그 예제에서는 단일 TextBlock을 사용하여 하이퍼링크를 보다 효율적으로 표시하는 방법을 보여 줍니다.

<!-- Hyperlinks combined in the same TextBlock. -->
<TextBlock>
  <Hyperlink TextDecorations="None" NavigateUri="http://www.msn.com">MSN Home</Hyperlink>
  
  <Run Text=" | " />
  
  <Hyperlink TextDecorations="None" NavigateUri="http://my.msn.com">My MSN</Hyperlink>
</TextBlock>

TextDecoration 개체는 텍스트에 추가할 수 있는 시각적 장식입니다. 그러나 인스턴스화하는 데 성능 집중적일 수 있습니다. Hyperlink 요소를 광범위하게 사용하는 경우 MouseEnter 이벤트와 같은 이벤트를 트리거하는 경우에만 밑줄을 표시하는 것이 좋습니다. 자세한 내용은 하이퍼링크에 밑줄이 그어지는지 여부 지정을 참조하세요.

다음 이미지는 MouseEnter 이벤트가 밑줄이 표시된 하이퍼링크를 트리거하는 방법을 보여 줍니다.

TextDecoration을 표시하는 하이퍼링크

다음 태그 샘플은 Hyperlink 와 밑줄 없이 정의 합니다.

<!-- Hyperlink with default underline. -->
<Hyperlink NavigateUri="http://www.msn.com">
  MSN Home
</Hyperlink>

<Run Text=" | " />

<!-- Hyperlink with no underline. -->
<Hyperlink Name="myHyperlink" TextDecorations="None"
           MouseEnter="OnMouseEnter"
           MouseLeave="OnMouseLeave"
           NavigateUri="http://www.msn.com">
  My MSN
</Hyperlink>

다음 표에서는 밑줄이 있거나 없는 1000개의 Hyperlink 요소를 표시할 때의 성능 비용을 보여 줍니다.

하이퍼링크 만든 시간(ms) 렌더링 시간(ms)
밑줄 있음 289 1130
밑줄 없음 299 776

텍스트 서식 지정 기능

WPF는 자동 하이픈 넣기 등 서식 있는 텍스트 서식 지정 서비스를 제공합니다. 이러한 서비스는 애플리케이션 성능에 영향을 줄 수 있으며 필요한 경우에만 사용해야 합니다.

불필요한 하이픈 넣기 사용 안 함

자동 하이픈 넣기는 텍스트 줄의 하이픈 중단점을 찾고 TextBlockFlowDocument 개체에서 줄의 추가적인 중단 위치를 허용합니다. 기본적으로 이러한 개체에서는 자동 하이픈 넣기 기능이 사용하지 않도록 설정됩니다. 개체의 IsHyphenationEnabled 속성을 true로 설정하여 이 기능을 사용하도록 설정할 수 있습니다. 그러나 이 기능을 사용하면 WPF에서 COM(구성 요소 개체 모델) 상호 운용성이 시작되어 애플리케이션 성능에 영향을 미칠 수 있습니다. 꼭 필요한 경우가 아니면 자동 하이픈 넣기를 사용하지 않는 것이 좋습니다.

신중하게 그림 사용

Figure 요소는 콘텐츠 페이지 내에서 절대적 위치를 지정할 수 있는 흐름 콘텐츠의 일부를 나타냅니다. Figure를 사용할 때 이미 배치된 콘텐츠와 위치가 충돌하는 경우 전체 페이지의 서식이 자동으로 다시 지정될 수 있습니다. Figure 요소를 서로 나란히 그룹화하거나 고정된 페이지 크기 시나리오에서 콘텐츠 위쪽에 선언하여 불필요한 서식 다시 지정을 최소화할 수 있습니다.

최적 단락

FlowDocument 개체의 최적 단락 기능은 공백이 가능한 한 균등하게 배분되도록 단락을 배치합니다. 기본적으로 최적 단락 기능은 사용하지 않도록 설정됩니다. 개체의 IsOptimalParagraphEnabled 속성을 true로 설정하여 이 기능을 사용하도록 설정할 수 있습니다. 그러나 이 기능을 사용하면 애플리케이션 성능에 영향을 미칩니다. 꼭 필요한 경우가 아니면 최적 단락 기능을 사용하지 않는 것이 좋습니다.

참고 항목