XAML 스타일

XAML 프레임워크를 사용하여 다양한 방법으로 앱 모양을 사용자 지정할 수 있습니다. 스타일을 사용하면 컨트롤 속성을 설정하고 이 설정을 재사용하여 여러 컨트롤에서 일관된 모양을 얻을 수 있습니다.

WinUI 및 스타일

WinUI 2.2부터 WinUI(Windows UI 라이브러리)를 사용하여 UI 구성 요소 간에 새로운 시각적 개체 스타일 업데이트를 제공했습니다. UI가 최신 스타일로 업데이트되지 않는 경우 최신 WinUI NuGet 패키지로 업데이트해야 합니다.

WinUI 2.6부터 대부분의 컨트롤에 대한 새로운 스타일과, 필요한 경우 이전 컨트롤 스타일로 되돌릴 수 있는 새 버전 관리 시스템을 제공합니다. 새 스타일이 Windows 디자인 방향과 좀 더 일치하기 때문에 이를 권장합니다. 그러나 사용자 시나리오에서 새 스타일을 지원할 수 없는 경우 이전 버전을 계속 사용할 수 있습니다.

WinUI 버전 2를 사용할 때 Application.Resources에 포함하는 XamlControlsResources에 대해 ControlsResourcesVersion 속성을 설정하여 스타일 버전을 변경할 수 있습니다. ControlsResourcesVersion은 기본적으로 열거형 값 Version2입니다.

이 값을 Version1로 설정하면 XamlControlsResources에서 최신 WinUI 버전에서 사용하는 새 스타일 대신 이전 스타일 버전을 로드합니다. 런타임에 이 속성을 변경하는 것은 지원되지 않으며 VisualStudio의 핫 다시 로드 기능은 작동하지 않습니다. 그러나 애플리케이션을 다시 빌드하면 컨트롤 스타일이 변경됩니다.

<Application.Resources>
    <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" 
                           ControlsResourcesVersion="Version1"/>
</Application.Resources>

스타일 기본 사항

스타일을 사용하여 재사용 가능한 리소스로 시각적 개체 속성의 설정을 추출합니다. 다음은 BorderBrush, BorderThickness포그라운드 속성을 설정하는 스타일이 있는 3개의 단추를 보여 주는 예제입니다. 스타일을 적용하면 각 컨트롤에서 이러한 속성을 별도로 설정하지 않고도 컨트롤이 동일하게 표시되도록 할 수 있습니다.

Screenshot of three styled buttons arranged side by side.

컨트롤의 XAML에서 스타일 인라인을 정의하거나 재사용 가능한 리소스로 정의할 수 있습니다. 개별 페이지의 XAML 파일, App.xaml 파일 또는 별도의 리소스 사전 XAML 파일에서 리소스를 정의합니다. 리소스 사전 XAML 파일은 앱 간에 공유할 수 있으며 단일 앱에서 둘 이상의 리소스 사전을 병합할 수 있습니다. 리소스가 정의된 위치에 따라 리소스를 사용할 수 있는 범위가 결정됩니다. 페이지 수준 리소스는 정의된 페이지에서만 사용할 수 있습니다. 키가 동일한 리소스가 App.xaml과 페이지에 모두 정의되어 있으면 페이지의 리소스가 App.xaml의 리소스를 재정의합니다. 리소스가 별도의 리소스 사전 파일에 정의된 경우 리소스 사전이 참조된 위치에 따라 범위가 결정됩니다.

Style 정의에는 TargetType 특성과 하나 이상의 Setter 요소 컬렉션이 필요합니다. TargetType 특성은 스타일을 적용하는 FrameworkElement 형식을 지정하는 문자열입니다. TargetType 값은 Windows 런타임 정의된 FrameworkElement 파생 형식 또는 참조된 어셈블리에서 사용할 수 있는 사용자 지정 형식을 지정해야 합니다. 컨트롤에 스타일을 적용하려고 하면 컨트롤의 형식이 적용하려는 스타일의 TargetType 특성과 일치하지 않으면 예외가 발생합니다.

Setter 요소에는 속성Value가 필요합니다. 이러한 속성 설정은 설정이 적용되는 컨트롤 속성과 해당 속성에 대해 설정할 값을 나타냅니다. 특성 또는 속성 요소 구문을 사용하여 Setter.Value를 설정할 수 있습니다. 여기서 XAML은 이전에 표시된 버튼으로 적용된 스타일을 보여 줍니다. 이 XAML에서 처음 두 Setter 요소는 특성 구문을 사용하지만, 마지막 SetterBorderBrush 속성에 대해 속성 요소 구문을 사용합니다. 이 예제에서는 x:Key 특성을 사용하지 않으므로 스타일이 버튼에 암시적으로 적용됩니다. 스타일을 암시적으로 또는 명시적으로 적용하는 방법은 다음 섹션에서 설명합니다.

<Page.Resources>
    <Style TargetType="Button">
        <Setter Property="BorderThickness" Value="5" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="BorderBrush" >
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="Yellow" Offset="0.0" />
                    <GradientStop Color="Red" Offset="0.25" />
                    <GradientStop Color="Blue" Offset="0.75" />
                    <GradientStop Color="LimeGreen" Offset="1.0" />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

<StackPanel Orientation="Horizontal">
    <Button Content="Button"/>
    <Button Content="Button"/>
    <Button Content="Button"/>
</StackPanel>

암시적 또는 명시적 스타일 적용

스타일을 리소스로 정의할 경우 다음 두 가지 방법으로 스타일을 컨트롤에 적용할 수 있습니다.

스타일에 x:Key 특성이 포함된 경우, 컨트롤의 Style 속성을 키 입력 스타일로 설정하여 이 특성을 컨트롤에만 적용할 수 있습니다. 반면, x:Key 특성이 없는 스타일은 대상 형식의 모든 컨트롤에 자동으로 적용되며, 그렇지 않으면 명시적 스타일 설정이 없습니다.

다음은 암시적 스타일과 명시적 스타일을 보여 주는 두 개의 버튼입니다.

implicitly and explicitly styled buttons.

이 예제에서는 첫 번째 스타일에 x:Key 특성이 있고 대상 유형은 Button입니다. 첫 번째 버튼의 Style 속성이 이 키로 설정되므로 이 스타일은 명명된 스타일이며 명시적으로 적용되어야 합니다. 두 번째 버튼은 대상 유형이 Button이고 스타일에 x:Key 특성이 없으므로 두 번째 스타일이 암시적으로 자동 적용됩니다.

<Page.Resources>
    <Style x:Key="PurpleStyle" TargetType="Button">
        <Setter Property="FontFamily" Value="Segoe UI"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Foreground" Value="Purple"/>
    </Style>

    <Style TargetType="Button">
        <Setter Property="FontFamily" Value="Segoe UI"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="RenderTransform">
            <Setter.Value>
                <RotateTransform Angle="25"/>
            </Setter.Value>
        </Setter>
        <Setter Property="BorderBrush" Value="Green"/>
        <Setter Property="BorderThickness" Value="2"/>
        <Setter Property="Foreground" Value="Green"/>
    </Style>
</Page.Resources>

<Grid x:Name="LayoutRoot">
    <Button Content="Button" Style="{StaticResource PurpleStyle}"/>
    <Button Content="Button"/>
</Grid>

기반 스타일 사용

스타일을 더 쉽게 기본 스타일 재사용을 최적화하려면 다른 스타일에서 상속되는 스타일을 만들 수 있습니다. BasedOn 속성을 사용하여 상속된 스타일을 만듭니다. 다른 스타일에서 상속되는 스타일은 동일한 형식의 컨트롤 또는 기본 스타일이 대상으로 하는 형식에서 파생되는 컨트롤을 대상으로 해야 합니다. 예를 들어 기본 스타일이 ContentControl을 대상으로 지정할 경우 이 스타일을 기반으로 하는 파생 스타일은 ContentControl, 또는 ButtonScrollViewer와 같은 ContentControl에서 파생된 유형을 대상으로 지정할 수 있습니다. 기준 스타일에서 값이 설정되지 않은 경우 기본 스타일에서 상속됩니다. 기본 스타일에서 값을 변경하려면 기반 스타일이 해당 값을 재정의합니다. 다음 예제는 동일한 기본 스타일에서 상속받은 스타일이 적용된 ButtonCheckBox를 보여줍니다.

styled buttons usign based-on styles.

기본 스타일은 ContentControl을 대상으로 하며 HeightWidth 속성을 설정합니다. 이 스타일을 기반으로 하는 스타일은 ContentControl에서 파생되는 CheckBoxButton을 대상으로 지정합니다. 기반 스타일은 BorderBrush포그라운드 속성에 대해 서로 다른 색을 설정합니다. (일반적으로 CheckBox 주위에 테두리를 배치하지는 않습니다. 여기서는 스타일의 효과를 보여 줍니다.)

<Page.Resources>
    <Style x:Key="BasicStyle" TargetType="ContentControl">
        <Setter Property="Width" Value="130" />
        <Setter Property="Height" Value="30" />
    </Style>

    <Style x:Key="ButtonStyle" TargetType="Button"
           BasedOn="{StaticResource BasicStyle}">
        <Setter Property="BorderBrush" Value="Orange" />
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="Foreground" Value="Red" />
    </Style>

    <Style x:Key="CheckBoxStyle" TargetType="CheckBox"
           BasedOn="{StaticResource BasicStyle}">
        <Setter Property="BorderBrush" Value="Blue" />
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="Foreground" Value="Green" />
    </Style>
</Page.Resources>

<StackPanel>
    <Button Content="Button" Style="{StaticResource ButtonStyle}" Margin="0,10"/>
    <CheckBox Content="CheckBox" Style="{StaticResource CheckBoxStyle}"/>
</StackPanel>

도구를 사용하여 쉽게 스타일 작업

컨트롤에 스타일을 적용하는 빠른 방법은 Microsoft Visual Studio XAML 디자인 화면에서 컨트롤을 마우스 오른쪽 단추로 클릭하고 Edit Style 또는 Edit Template을 선택하는 것입니다(마우스 오른쪽 단추로 클릭하는 컨트롤에 따라 다름). 그런 다음 Apply Resource를 선택하여 기존 스타일을 적용하거나 Create Empty를 선택하여 새 스타일을 정의할 수 있습니다. 빈 스타일을 만드는 경우 페이지, App.xaml 파일 또는 별도의 리소스 사전에서 정의할 수 있는 옵션이 제공됩니다.

경량 스타일링

시스템 브러시를 재정의하는 작업은 일반적으로 앱 또는 페이지 수준에서 수행되며, 두 경우 모두 해당 브러시를 참조하는 모든 컨트롤에 색상 재지정이 적용됩니다. 또한 XAML에서는 여러 컨트롤이 동일한 시스템 브러시를 참조할 수 있습니다.

Screenshot of two buttons: one in its rest state and one with lightweight Styling applied.

<Page.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Light">
                 <SolidColorBrush x:Key="ButtonBackground" Color="Transparent"/>
                 <SolidColorBrush x:Key="ButtonForeground" Color="MediumSlateBlue"/>
                 <SolidColorBrush x:Key="ButtonBorderBrush" Color="MediumSlateBlue"/>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
</Page.Resources>

PointerOver(마우스가 버튼 위에 마우스로 가리키고 있음), PointerPressed (버튼이 호출됨) 또는 사용 안 함(버튼이 상호 작용할 수 없음)과 같은 상태의 경우 이러한 끝은 원래 경량 스타일 이름인 ButtonBackgroundPointerOver, ButtonForegroundPressed, ButtonBorderBrushDisabled 등에 추가됩니다. 이러한 브러시를 수정하면 컨트롤이 앱의 테마에 일관되게 색이 지정됩니다.

이러한 브러시 재정의를 App.Resources 수준에서 배치하면 단일 페이지가 아닌 전체 앱 내의 모든 버튼이 변경됩니다.

컨트롤별 스타일 지정

다른 경우에는 해당 컨트롤의 다른 버전을 변경하지 않고 특정 방식으로만 한 페이지에서 단일 컨트롤을 변경하는 것이 바람직합니다.

Screenshot of three styled buttons arranged stacked one on top of the other.

<CheckBox Content="Normal CheckBox" Margin="5"/>
<CheckBox Content="Special CheckBox" Margin="5">
    <CheckBox.Resources>
        <ResourceDictionary>
            <ResourceDictionary.ThemeDictionaries>
                <ResourceDictionary x:Key="Light">
                    <SolidColorBrush x:Key="CheckBoxForegroundUnchecked"
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxForegroundChecked"
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxCheckGlyphForegroundChecked"
                        Color="White"/>
                    <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeChecked"  
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxCheckBackgroundFillChecked"
                        Color="Purple"/>
                </ResourceDictionary>
            </ResourceDictionary.ThemeDictionaries>
        </ResourceDictionary>
    </CheckBox.Resources>
</CheckBox>
<CheckBox Content="Normal CheckBox" Margin="5"/>

이것은 해당 컨트롤이 있는 페이지의 단일 '특수 CheckBox'에만 영향을 미칩니다.

사용자 지정 컨트롤

기본 제공 컨트롤과 시각적으로 정렬되거나 기능적으로 정렬될 수 있는 사용자 지정 컨트롤을 빌드하는 경우 암시적 스타일 지정 및 경량 스타일 지정 리소스를 사용하여 사용자 지정 콘텐츠를 정의하는 것이 좋습니다. 리소스를 직접 사용하거나 리소스에 대한 새 별칭을 만들 수 있습니다.

컨트롤 리소스 직접 사용

예를 들어 버튼처럼 보이는 컨트롤을 작성하는 경우 다음과 같이 컨트롤이 버튼 리소스를 직접 참조하게 할 수 있습니다.

<Style TargetType="local:MyCustomControl">
  <Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
  <Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
</Style>

컨트롤 리소스에 새 별칭 지정

또는 고유한 리소스를 만들려는 경우 해당 사용자 지정 이름을 기본 경량 스타일 지정 리소스에 별칭으로 지정해야 합니다.

예를 들어 사용자 지정 컨트롤의 스타일에는 특별한 리소스 정의가 있을 수 있습니다.

<Style TargetType="local:MyCustomControl">
  <Setter Property="Background" Value="{ThemeResource MyCustomControlBackground}" />
  <Setter Property="BorderBrush" Value="{ThemeResource MyCustomControlBorderBrush}"/>
</Style>

리소스 사전 또는 기본 정의에서 경량 스타일 지정 리소스를 사용자 지정 리소스에 연결합니다.

<ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Default">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>        
    <ResourceDictionary x:Key="Light">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

세 가지 다른 테마 변경 내용(Default, Light, HighContrast)을 제대로 처리하기 위해 복제된 ThemeDictionary을(를) 세 번 사용해야 합니다.

주의

새 별칭에 경량 스타일 지정 리소스를 할당하고 경량 스타일 지정 리소스를 다시 정의하는 경우 리소스 조회가 올바른 순서가 아니면 사용자 지정이 적용되지 않을 수 있습니다. 예를 들어 MyCustomControlBackground이(가) 발견되기 전에 검색된 지점에서 ButtonBackground을(를) 재정의하면 해당 재정의가 누락됩니다.

컨트롤 스타일 다시 지정 방지

Windows UI 라이브러리 2.2 이상에는 WinUI 및 시스템 컨트롤 모두에 대한 새로운 스타일과 템플릿이 포함되어 있습니다.

최신 시각적 개체 스타일을 최신 상태로 유지하는 가장 좋은 방법은 최신 WinUI 2 패키지를 사용하고 사용자 지정 스타일 및 템플릿(템플릿 재작성)을 방지하는 것입니다. 스타일은 앱의 여러 컨트롤에서 값 집합을 일관되게 적용하는 편리한 방법입니다. 이 작업을 수행하는 경우 최신 스타일을 기준으로 해야 합니다.

WinUI 스타일(Windows.UI.Xaml.Controls 네임스페이스)을 사용하는 시스템 컨트롤의 경우 BasedOn="{StaticResource Default<ControlName>Style}"을(를) 설정합니다. 여기서 <ControlName>은(는) 컨트롤의 이름입니다. 예시:

<Style TargetType="TextBox" BasedOn="{StaticResource DefaultTextBoxStyle}">
    <Setter Property="Foreground" Value="Blue"/>
</Style>

WinUI 2 컨트롤(Microsoft.UI.Xaml.Controls 네임스페이스)의 경우 기본 스타일이 메타데이터에 정의되므로 BasedOn을(를) 생략합니다.

파생 컨트롤

기존 XAML 컨트롤에서 사용자 지정 컨트롤을 파생하는 경우 기본적으로 WinUI 2 스타일이 제공되지 않습니다. WinUI 2 스타일을 적용하려면 다음을 수행합니다.

  • TargetType이 사용자 지정 컨트롤로 설정된 새 Style을 만듭니다.
  • Style을 파생한 컨트롤의 기본 스타일로 사용합니다.

이에 대한 일반적인 시나리오 중 하나는 ContentDialog에서 새 컨트롤을 파생하는 것입니다. 이 예제에서는 사용자 지정 대화 상자에 DefaultContentDialogStyle을(를) 적용하는 새 Style을 만드는 방법을 보여 줍니다.

<ContentDialog
    x:Class="ExampleApp.SignInContentDialog"
    ... >

    <ContentDialog.Resources>
        <Style TargetType="local:SignInContentDialog" BasedOn="{StaticResource DefaultContentDialogStyle}"/>
        ...
    </ContentDialog.Resources> 
    <!-- CONTENT -->
</ContentDialog>        

Template 속성

스타일 setter는 ControlTemplate 속성에 사용할 수 있으며 사실 대부분의 일반적인 XAML 스타일과 앱의 XAML 리소스를 구성합니다. 이 스타일 setter는 컨트롤 템플릿에서 더 자세히 알아봅니다.