XAML 테마 리소스

XAML의 테마 리소스는 활성 상태인 시스템 테마에 따라 다른 값을 적용하는 리소스 세트입니다. XAML 프레임워크에서 지원하는 세 가지 테마는 '밝게', '어둡게' 및 '고대비'입니다.

필수 구성 요소: 이 항목에서는 ResourceDictionary 및 XAML 리소스 참조를 읽었다고 가정합니다.

테마 리소스와 정적 리소스 비교

기존 XAML 리소스 사전에서 XAML 리소스를 참조할 수 있는 두 개의 XAML 마크업 확장이 있습니다. {StaticResource} 마크업 확장{ThemeResource} 마크업 확장입니다.

{ThemeResource} 마크업 확장 평가는 앱이 로드되고 이후에 런타임에 테마가 변경 될 때마다 발생합니다. 이는 일반적으로 사용자가 디바이스 설정을 변경하거나 현재 테마를 변경하는 앱 내에서 프로그래밍 방식으로 변경한 결과입니다.

반면, {StaticResource} 마크업 확장 은 앱에서 XAML을 처음 로드할 때만 평가됩니다. 업데이트되지 않음 XAML에서 앱 시작 시 실제 런타임 값으로 바꾸는 것과 비슷합니다.

리소스 사전 구조의 테마 리소스

각 테마 리소스는 XAML 파일 themeresources.xaml의 부분이 됩니다. 디자인 목적으로 themeresources.xaml은 Windows SDK(소프트웨어 개발 키트) 설치의 \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<SDK version>\Generic 폴더에 제공됩니다. themeresources.xaml의 리소스 사전 또한 동일한 디렉터리의 generic.xaml에서 재현됩니다.

Windows 런타임은 런타임 조회를 위해 이러한 물리적 파일을 사용하지 않습니다. 따라서 이는 DesignTime 폴더에 있으며 기본적으로 앱에 복사되지 않습니다. 대신 이러한 리소스 사전은 Windows 런타임 자체의 일부로 메모리에 존재하며, 런타임에 테마 리소스(또는 시스템 리소스)에 대한 앱의 XAML 리소스 참조가 해결됩니다.

사용자 지정 테마 리소스에 대한 지침

고유한 사용자 지정 테마 리소스를 정의하여 사용하는 경우 다음 지침을 따르세요.

  • '고대비' 사전 외에 '밝게' 및 '어둡게' 모두에 대한 테마 사전을 지정합니다. 'Default'를 키로 사용하여 ResourceDictionary를 만들 수 있지만, 명시적으로 '밝게', '어둡게' 및 '고대비'를 대신 사용하는 것이 좋습니다.

  • 스타일, Setter, 컨트롤 템플릿, 속성 setter 및 애니메이션에서 {ThemeResource} 마크업 확장을 사용합니다.

  • ThemeDictionaries 내의 리소스 정의에는 {ThemeResource} 마크업 확장을 사용하지 마세요. 대신 {StaticResource} 마크업 확장을 사용합니다.

    예외: {ThemeResource} 마크업 확장을 사용하여 ThemeDictionaries의 앱 테마에 관계없이 리소스를 참조할 수 있습니다. 이러한 리소스의 예로는 테마 컬러 리소스(예: SystemAccentColor와(과) 같은 시스템 색 리소스)가 있으며, 일반적으로 SystemColorButtonFaceColor 'SystemColor'와 같은 접두사라고 합니다.

주의

다음 지침을 따르지 않으면 앱에서 테마와 관련된 예기치 않은 동작이 발생할 수 있습니다. 자세한 테마 리소스 문제 해결 정보는 섹션을 참조하세요.

XAML 색 램프 및 테마 종속 브러시

'밝게', '어둡게' 및 '고대비' 테마의 결합된 색 집합은 XAML에서 Windows 색 램프를 구성합니다. 시스템 테마를 수정하거나 테마를 고유한 XAML 요소에 적용하려면 색 리소스의 구성 방식을 이해해야 합니다.

Windows 앱에 색을 적용하는 방법에 대한 자세한 내용은 Windows 앱의 색을 참조하세요.

밝은 테마 및 어두운 테마 색상

XAML 프레임워크는 '밝은' 및 '어두운' 테마에 맞게 조정된 값으로 명명된 Color 리소스 집합을 제공합니다. WinUI 2의 경우 테마 리소스는 공통 테마 리소스 Xaml 파일에 정의되어 있습니다. 색 이름은 용도를 매우 잘 설명하고 있으며 모든 Color 리소스에 해당하는 SolidColorBrush 리소스가 있습니다.

이러한 색의 시각적 개요는 WinUI 3 갤러리 앱: 을 참조하세요.

WinUI 3 갤러리 앱에는 대부분의 WinUI 3 컨트롤, 특징, 기능의 대화형 예제가 포함되어 있습니다. Microsoft Store에서 앱을 다운로드하거나 GitHub에서 소스 코드를 가져오세요.

Windows 시스템 대비 테마 색

XAML 프레임워크에서 제공하는 리소스 집합 외에도 Windows 시스템 팔레트에서 파생된 색 값 세트가 있습니다. 이러한 색은 Windows 런타임 또는 Windows 앱에만 국한되지 않습니다. 그러나 대부분의 XAML 브러시 리소스는 '고대비' 테마를 사용하여 시스템이 작동하고 앱이 실행 중일 때 이러한 색을 사용합니다. XAML 프레임워크는 이러한 시스템 차원의 색을 키 리소스로 제공합니다. 키는 이름 지정 형식 SystemColor[name]Color을(를) 따릅니다.

대비 테마 지원에 대한 자세한 내용은 대비 테마를 참조하세요.

시스템 강조 색

시스템 대비 테마 색 외에도 시스템 테마 색이 SystemAccentColor 키를 사용하여 특수한 색 리소스로 제공됩니다. 런타임에 이 리소스는 사용자가 Windows 개인 설정에서 강조 색으로 지정한 색을 가져옵니다.

참고

시스템 색 리소스를 재정의할 수 있지만, 특히 대비 테마 설정의 경우 사용자의 색 선택 항목을 적용하는 것이 가장 좋습니다.

테마 종속 브러시

위 섹션에 표시된 색 리소스는 시스템 테마 리소스 사전에서 SolidColorBrush 리소스의 Color 속성을 설정하는 데 사용됩니다. 브러시 리소스를 사용하여 XAML 요소에 색을 적용합니다.

런타임에 이 브러시에 대한 색 값이 결정되는 방법을 살펴보겠습니다. '밝게' 및 '어둡게' 리소스 사전에서 이 브러시는 다음과 같이 정의됩니다.

<SolidColorBrush x:Key="TextFillColorPrimaryBrush" Color="{StaticResource TextFillColorPrimary}"/>

'고대비' 리소스 사전에서 이 브러시는 다음과 같이 정의됩니다.

<SolidColorBrush x:Key="TextFillColorPrimaryBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>

이 브러시가 XAML 요소에 적용되면 이 테이블과 같이 현재 테마에 의해 런타임에 해당 색이 결정됩니다.

테마 색 리소스 런타임 값
밝게 TextFillColorPrimary #E4000000
어둡게 TextFillColorPrimary #FFFFFFFF
고대비 SystemColorWindowTextColor 텍스트 설정에 지정된 색입니다.

XAML 유형 램프

themeresources.xaml 파일은 UI의 텍스트 컨테이너, 특히 TextBlock 또는 RichTextBlock에 적용할 수 있는 Style을 정의하는 몇 가지 리소스를 정의합니다. 이것은 기본 암시적 스타일이 아닙니다. 글꼴 가이드라인에 설명Windows 유형 램프와 일치하는 XAML UI 정의를 더 쉽게 만들 수 있도록 제공됩니다.

이런 스타일은 전체 텍스트 컨테이너에 적용하려는 텍스트 특성과 관련됩니다. 텍스트의 일부에만 스타일을 적용하려는 경우에는 컨테이너 내부 텍스트 요소에, 예를 들어 TextBlock.InlinesRun 또는 RichTextBlock.BlocksParagraph에 대해 특성을 설정합니다.

스타일은 TextBlock에 적용될 때 다음과 같이 표시됩니다.

text block styles

스타일 가중치 크기
캡션 일반 12
본문 일반 14
본문 Strong 약간 굵게 14
본문 Large 일반 18
부제목 약간 굵게 20
타이틀 약간 굵게 28
제목 Large 약간 굵게 40
표시 약간 굵게 68
<TextBlock Text="Caption" Style="{StaticResource CaptionTextBlockStyle}"/>
<TextBlock Text="Body" Style="{StaticResource BodyTextBlockStyle}"/>
<TextBlock Text="Body Strong" Style="{StaticResource BodyStrongTextBlockStyle}"/>
<TextBlock Text="Body Large" Style="{StaticResource BodyLargeTextBlockStyle}"/>
<TextBlock Text="Subtitle" Style="{StaticResource SubtitleTextBlockStyle}"/>
<TextBlock Text="Title" Style="{StaticResource TitleTextBlockStyle}"/>
<TextBlock Text="Title Large" Style="{StaticResource TitleLargeTextBlockStyle}"/>
<TextBlock Text="Display" Style="{StaticResource DisplayTextBlockStyle}"/>

앱에서 Windows 유형 램프를 사용하는 방법에 대한 지침은 Windows 앱의 입력 체계를 참조하세요.

XAML 스타일에 대한 자세한 내용은 GitHub의 WinUI를 참조하세요.

이러한 스타일의 시각적 개요는 WinUI 3 갤러리 앱: 타이포그래피을 참조하세요.

BaseRichTextBlockStyle

TargetType: RichTextBlock

다른 모든 RichTextBlock 컨테이너 스타일에 대한 공통 속성을 제공합니다.

<!-- Usage -->
<RichTextBlock Style="{StaticResource BaseRichTextBlockStyle}">
    <Paragraph>Rich text.</Paragraph>
</RichTextBlock>

<!-- Style definition -->
<Style x:Key="BaseRichTextBlockStyle" TargetType="RichTextBlock">
    <Setter Property="FontFamily" Value="Segoe UI"/>
    <Setter Property="FontWeight" Value="SemiBold"/>
    <Setter Property="FontSize" Value="14"/>
    <Setter Property="TextTrimming" Value="None"/>
    <Setter Property="TextWrapping" Value="Wrap"/>
    <Setter Property="LineStackingStrategy" Value="MaxHeight"/>
    <Setter Property="TextLineBounds" Value="Full"/>
    <Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
</Style>

BodyRichTextBlockStyle

<!-- Usage -->
<RichTextBlock Style="{StaticResource BodyRichTextBlockStyle}">
    <Paragraph>Rich text.</Paragraph>
</RichTextBlock>

<!-- Style definition -->
<Style x:Key="BodyRichTextBlockStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaseRichTextBlockStyle}">
    <Setter Property="FontWeight" Value="Normal"/>
</Style>

참고: RichTextBlock 스타일에는 TextBlock에 있는 텍스트 램프 스타일 중 일부가 없습니다. 이는 주로 RichTextBlock에 대한 블록 기반 문서 개체 모델을 사용하면 개별 텍스트 요소에 대한 특성을 더 쉽게 설정할 수 있기 때문입니다. 또한 XAML 콘텐츠 속성을 사용하여 TextBlock.Text를 설정하면 스타일을 지정할 텍스트 요소가 없으므로 컨테이너의 스타일을 지정해야 하는 상황이 발생합니다. RichTextBlock의 경우에는 문제가 되지 않습니다. 해당 텍스트 콘텐츠가 페이지 머리글, 페이지 하위 머리글, 유사한 텍스트 램프 정의에 대해 XAML 스타일을 적용하는 Paragraph 같은 특정 텍스트 요소에 항상 있어야 하기 때문입니다.

기타 명명된 스타일

기본 암시적 스타일과는 다르게 Button 스타일을 지정하기 위해 적용할 수 있는 추가적인 키 입력 Style 정의 집합이 있습니다.

TargetType: Button

스타일은 탐색 앱의 탐색 뒤로 버튼이 될 수 있는 Button 에 대한 전체 템플릿을 제공합니다. 기본 차원은 40 x 40 픽셀입니다. 스타일을 조정하기 위해 Button에 대해 Height, Width, FontSize 및 다른 속성을 명시적으로 설정하거나, BasedOn을 사용하여 파생 스타일을 만들 수 있습니다.

다음은 NavigationBackButtonNormalStyle 리소스가 적용된 Button입니다.

<Button Style="{StaticResource NavigationBackButtonNormalStyle}" />

앱은 다음과 같습니다.

A button styled as a back button

TargetType: Button

스타일은 탐색 앱의 탐색 뒤로 버튼이 될 수 있는 Button 에 대한 전체 템플릿을 제공합니다. NavigationBackButtonNormalStyle과 비슷하지만, 치수가 30x30 픽셀입니다.

다음은 NavigationBackButtonSmallStyle 리소스가 적용된 Button입니다.

<Button Style="{StaticResource NavigationBackButtonSmallStyle}" />

테마 리소스 문제 해결

테마 리소스 사용 가이드라인을 따르지 않으면 앱에서 테마와 관련된 예기치 않은 동작이 발생할 수 있습니다.

예를 들어 밝은 테마 플라이아웃을 열면 어두운 테마 앱의 일부도 밝은 테마에 있는 것처럼 변경됩니다. 또는 밝은 테마 페이지로 이동한 다음 다시 탐색하는 경우, 원래 어두운 테마 페이지(또는 그 일부)는 이제 밝은 테마에 있는 것처럼 보입니다.

일반적으로 이러한 유형의 문제는 고대비 시나리오를 지원하기 위해 '기본' 테마와 '고대비' 테마를 제공한 다음 앱의 다른 부분에서 '밝게' 및 '어둡게' 테마를 모두 사용할 때 발생합니다.

예를 들어 다음 테마 사전 정의를 고려해 보시길 바랍니다.

<!-- DO NOT USE. THIS XAML DEMONSTRATES AN ERROR. -->
<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Default">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

직관적으로 보았을 때, 이것은 정확해 보입니다. 고대비일 때 가리키는 myBrush 색을 변경하려고 하지만 고대비가 아닌 경우 {ThemeResource} 마크업 확장을 사용하여 테마에 적합한 색을 myBrush이(가) 가리키도록 합니다. 앱에 시각적 개체 트리 내의 요소에 설정된 FrameworkElement.RequestedTheme 가 없는 경우 일반적으로 예상대로 작동합니다. 그러나 시각적 개체 트리의 다른 부분에 테마를 다시 지정하기 시작하자마자 앱에서 문제가 발생합니다.

브러시는 대부분의 다른 XAML 형식과 달리 공유 리소스이기 때문에 문제가 발생합니다. 동일한 브러시 리소스를 참조하는 테마가 다른 XAML 하위 트리에 요소가 2개 있는 경우 프레임워크가 각 하위 트리를 따라 {ThemeResource} 마크업 확장 식을 업데이트할 때 공유 브러시 리소스에 대한 변경 내용은 의도한 결과가 아닌 다른 하위 트리에 반영됩니다.

이 문제를 해결하려면 '기본' 사전을 '고대비' 외에도 '밝게' 및 '어둡게' 테마 모두에 대한 별도의 테마 사전으로 바꿉니다.

<!-- DO NOT USE. THIS XAML DEMONSTRATES AN ERROR. -->
<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Light">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="Dark">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

그러나 이러한 리소스가 포그라운드와 같은 상속된 속성에서 참조되는 경우에도 문제가 발생합니다. 사용자 지정 컨트롤 템플릿은 {ThemeResource} 마크업 확장을 사용하여 요소의 포그라운드 색상을 지정할 수 있지만 프레임워크가 상속된 값을 자식 요소에 전파하면 {ThemeResource} 마크업 확장 식으로 확인된 리소스에 대한 직접 참조를 제공합니다. 이렇게 하면 프레임워크가 컨트롤의 시각적 개체 트리를 따라 이동하면서 테마 변경 내용을 처리할 때 문제가 발생합니다. 프레임워크는 {ThemeResource} 태그 확장 식을 다시 평가하여 새 브러시 리소스를 가져오지만, 아직 이 참조를 컨트롤의 자식으로 전파하지 않습니다. 이는 나중에, 예를 들어 다음 측정 단계 도중에 발생합니다.

따라서 테마 변경에 대한 응답으로 컨트롤 시각적 개체 트리를 걷고 나면 프레임워크는 자식을 안내하고 설정된 {ThemeResource} 마크업 확장 식 또는 속성에 설정된 개체를 업데이트합니다. 문제가 발생하는 위치입니다. 프레임워크는 브러시 리소스를 안내하고 {ThemeResource} 마크업 확장을 사용하여 해당 색을 지정하기 때문에 다시 평가됩니다.

이 시점에서 프레임워크는 이제 다른 사전에서 색이 설정된 한 사전의 리소스를 가지고 있기 때문에 테마 사전을 오염시킨 것으로 보입니다.

이 문제를 해결하려면 {ThemeResource} 마크업 확장 대신 {StaticResource} 마크업 확장을 사용합니다. 가이드라인이 적용되면 테마 사전은 다음과 같습니다.

<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Light">
      <SolidColorBrush x:Key="myBrush" Color="{StaticResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="Dark">
      <SolidColorBrush x:Key="myBrush" Color="{StaticResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

{ThemeResource} 마크업 확장{StaticResource} 마크업 확장 대신 '고대비' 사전에 계속 사용됩니다. 이 상황은 가이드라인의 앞부분에서 지정한 예외에 해당합니다. '고대비' 테마에 사용되는 대부분의 브러시 값은 시스템에서 전체적으로 제어하는 색 선택 항목을 사용하지만 XAML에는 특수하게 명명된 리소스(이름 앞에 'SystemColor'가 붙은 리소스)로 공개됩니다. 시스템을 사용하면 사용자가 접근성 센터를 통해 대비 테마 설정에 사용해야 하는 특정 색을 설정할 수 있습니다. 이러한 색 선택은 특별히 명명된 리소스에 적용됩니다. 또한 동일한 테마 변경 이벤트를 사용하여 시스템 수준에서 변경되었음이 검색되면 XAML 프레임워크에서 이러한 브러시를 업데이트합니다. {ThemeResource} 마크업 확장이 여기에 사용되는 이유입니다.