컨트롤 템플릿

XAML 프레임워크에서 컨트롤 템플릿을 만들어 컨트롤의 시각적 구조 및 시각적 동작을 사용자 지정할 수 있습니다. 컨트롤에는 백그라운드, 포그라운드FontFamily와 같은 많은 속성이 있으므로 컨트롤의 모양에 다른 측면을 지정하도록 설정할 수 있습니다. 그러나 이러한 속성을 설정하여 수행할 수 있는 변경에는 제한 사항이 있습니다. ControlTemplate 클래스를 사용하여 템플릿을 만들어 추가 사용자 지정을 지정할 수 있습니다. 여기서는 ControlTemplate을 만들어 CheckBox 컨트롤의 모양을 사용자 지정하는 방법을 보여줍니다.

중요 APIs: ControlTemplate 클래스, Control.Template 속성

사용자 지정 컨트롤 템플릿 예제

기본적으로 CheckBox 컨트롤은 해당 콘텐츠(CheckBox 옆의 문자열 또는 개체)를 선택 상자 오른쪽에 배치하고 검사 표시는 사용자가 CheckBox를 선택했음을 나타냅니다. 이러한 특성은 CheckBox의 시각적 구조 및 시각적 동작을 나타냅니다.

다음은 ,Unchecked, Checked, 및 Indeterminate 상태에 표시된 기본 ControlTemplate을 사용하는 CheckBox입니다.

default checkbox template

CheckBox에 대한 ControlTemplate을 만들어 이러한 특성을 변경할 수 있습니다. 예를 들어 검사 상자의 내용을 선택 상자 아래에 표시하고X를 사용하여 사용자가 검사 상자를 선택했음을 나타내려는 경우입니다. 이러한 특성은 CheckBoxControlTemplate에 지정합니다.

컨트롤과 함께 사용자 지정 템플릿을 사용하려면 ControlTemplate을 컨트롤의 Template 속성에 할당합니다. 다음은 CheckBoxTemplate1이라는 ControlTemplate을 사용하는 CheckBox입니다. 다음 섹션에서 ControlTemplate에 대한 XAML(Extensible Application Markup Language)을 보여 줍니다.

<CheckBox Content="CheckBox" Template="{StaticResource CheckBoxTemplate1}" IsThreeState="True" Margin="20"/>

템플릿을 적용한 후 이 CheckBoxUnchecked, CheckedIndeterminate 상태는 다음과 같습니다.

custom checkbox template

컨트롤의 시각적 구조 구체화

ControlTemplate을 만들 때 FrameworkElement 개체를 결합하여 단일 컨트롤을 빌드합니다. ControlTemplate에는 하나의 FrameworkElement만 루트 요소로 있어야 합니다. 그러나 루트 요소는 일반적으로 다른 FrameworkElement 개체를 포함합니다. 개체가 조합되면 컨트롤의 시각적 구조가 만들어집니다.

이 XAML은 컨트롤의 내용이 선택 상자 아래에 있음을 지정하는 CheckBox에 대한 ControlTemplate을 만듭니다. 루트 요소는 Border입니다. 이 예제에서는 사용자가 CheckBox를 선택했음을 나타내는 X를 만드는 Path와 확정되지 않은 상태를 나타내는 Ellipse를 지정합니다. Opacity는 기본적으로 둘 다 표시되지 않도록 PathEllipse에서 0으로 설정됩니다.

TemplateBinding은 컨트롤 템플릿의 속성 값을 템플릿 기반 컨트롤에서 노출되는 몇몇 다른 속성 값에 연결하는 특별 바인딩입니다. TemplateBinding은 XAML의 ControlTemplate 정의 내에서만 사용할 수 있습니다. 자세한 내용은 TemplateBinding 마크업 확장을 참조하세요.

참고

Windows 10 버전 1809(SDK 17763)부터 TemplateBinding을 사용하는 위치에 x:Bind 태그 확장을 사용할 수 있습니다. 자세한 내용은 TemplateBinding 마크업 확장을 참조하세요.

<ControlTemplate x:Key="CheckBoxTemplate1" TargetType="CheckBox">
    <Border BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            Background="{TemplateBinding Background}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="25"/>
            </Grid.RowDefinitions>
            <Rectangle x:Name="NormalRectangle" Fill="Transparent" Height="20" Width="20"
                       Stroke="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                       StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}"
                       UseLayoutRounding="False"/>
            <!-- Create an X to indicate that the CheckBox is selected. -->
            <Path x:Name="CheckGlyph"
                  Data="M103,240 L111,240 119,248 127,240 135,240 123,252 135,264 127,264 119,257 111,264 103,264 114,252 z"
                  Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
                  FlowDirection="LeftToRight"
                  Height="14" Width="16" Opacity="0" Stretch="Fill"/>
            <Ellipse x:Name="IndeterminateGlyph"
                     Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
                     Height="8" Width="8" Opacity="0" UseLayoutRounding="False" />
            <ContentPresenter x:Name="ContentPresenter"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              Content="{TemplateBinding Content}"
                              Margin="{TemplateBinding Padding}" Grid.Row="1"
                              HorizontalAlignment="Center"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Grid>
    </Border>
</ControlTemplate>

컨트롤의 시각적 동작 구체화

시각적 동작은 컨트롤이 특정 상태에 있을 때의 컨트롤 모양을 지정합니다. CheckBox 컨트롤에는 Checked, Unchecked, 및 Indeterminate의 3개의 검사 상태가 있습니다. IsChecked 속성의 값은 CheckBox의 상태를 결정하며 해당 상태는 상자에 표시되는 항목을 결정합니다.

이 표에는 IsChecked의 가능한 값, CheckBox의 해당 상태 및 CheckBox의 모양이 나열되어 있습니다.

IsChecked CheckBox 상태 CheckBox 모양
true Checked 'X'를 포함합니다.
false Unchecked 비어 있음.
null Indeterminate 원을 포함합니다.

VisualState 개체를 사용하여 컨트롤이 특정 상태일 때 컨트롤의 모양을 지정합니다. VisualState에는 ControlTemplate의 요소 모양을 변경하는 Setter 또는Storyboard가 포함되어 있습니다. 컨트롤이 VisualState.Name 속성이 지정하는 상태로 들어가면 Setter 또는 Storyboard의 속성 변경 내용이 적용됩니다. 컨트롤이 상태를 종료하면 변경 내용이 제거됩니다. VisualState 개체를 VisualStateGroup 개체에 추가합니다. VisualStateGroup 개체를 ControlTemplate의 루트 FrameworkElement에 설정한 VisualStateManager.VisualStateGroups 연결된 속성에 추가합니다.

이 XAML은 Checked, UncheckedIndeterminate 상태의 VisualState 개체를 보여 줍니다. 다음은 ControlTemplate의 루트 요소Border에서 VisualStateManager.VisualStateGroups 연결된 속성을 설정하는 예제입니다. CheckedVisualStateCheckGlyph라는 PathOpacity를 1로 지정합니다(이전 예제에 나와 있음). IndeterminateVisualStateIndeterminateGlyph이라는 EllipseOpacity를 1로 지정합니다. UncheckedVisualState에는 Setter 또는 Storyboard가 없으므로 CheckBox가 기본 모양으로 돌아갑니다.

<ControlTemplate x:Key="CheckBoxTemplate1" TargetType="CheckBox">
    <Border BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            Background="{TemplateBinding Background}">

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CheckStates">
                <VisualState x:Name="Checked">
                    <VisualState.Setters>
                        <Setter Target="CheckGlyph.Opacity" Value="1"/>
                    </VisualState.Setters>
                    <!-- This Storyboard is equivalent to the Setter. -->
                    <!--<Storyboard>
                        <DoubleAnimation Duration="0" To="1"
                         Storyboard.TargetName="CheckGlyph" Storyboard.TargetProperty="Opacity"/>
                    </Storyboard>-->
                </VisualState>
                <VisualState x:Name="Unchecked"/>
                <VisualState x:Name="Indeterminate">
                    <VisualState.Setters>
                        <Setter Target="IndeterminateGlyph.Opacity" Value="1"/>
                    </VisualState.Setters>
                    <!-- This Storyboard is equivalent to the Setter. -->
                    <!--<Storyboard>
                        <DoubleAnimation Duration="0" To="1"
                         Storyboard.TargetName="IndeterminateGlyph" Storyboard.TargetProperty="Opacity"/>
                    </Storyboard>-->
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="25"/>
            </Grid.RowDefinitions>
            <Rectangle x:Name="NormalRectangle" Fill="Transparent" Height="20" Width="20"
                       Stroke="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                       StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}"
                       UseLayoutRounding="False"/>
            <!-- Create an X to indicate that the CheckBox is selected. -->
            <Path x:Name="CheckGlyph"
                  Data="M103,240 L111,240 119,248 127,240 135,240 123,252 135,264 127,264 119,257 111,264 103,264 114,252 z"
                  Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
                  FlowDirection="LeftToRight"
                  Height="14" Width="16" Opacity="0" Stretch="Fill"/>
            <Ellipse x:Name="IndeterminateGlyph"
                     Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
                     Height="8" Width="8" Opacity="0" UseLayoutRounding="False" />
            <ContentPresenter x:Name="ContentPresenter"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              Content="{TemplateBinding Content}"
                              Margin="{TemplateBinding Padding}" Grid.Row="1"
                              HorizontalAlignment="Center"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Grid>
    </Border>
</ControlTemplate>

VisualState 개체의 작동 방식을 더 잘 이해하려면 CheckBoxUnchecked 상태에서 Checked 상태로, 다음 Indeterminate 상태로 , 다시 Unchecked 상태로 돌아가면 어떻게 되는지 고려합니다. 다음은 전환입니다.

상태 전환 수행되는 작업 전환이 완료되면 CheckBox 모양
Unchecked ~ Checked입니다. CheckedVisualStateSetter 값이 적용되므로 CheckGlyphOpacity는 1입니다. X가 표시됩니다.
Checked ~ Indeterminate입니다. IndeterminateVisualStateSetter 값이 적용되므로 IndeterminateGlyphOpacity는 1입니다. CheckedVisualStateSetter 값이 제거되므로 CheckGlyphOpacity 는 0입니다. 원이 표시됩니다.
Indeterminate ~ Unchecked입니다. IndeterminateVisualStateSetter 값이 제거되므로 IndeterminateGlyphOpacity는 0입니다. 아무 것도 표시되지 않습니다.

  컨트롤에 대한 시각적 상태를 만드는 방법, 특히 Storyboard 클래스 및 애니메이션 형식을 사용하는 방법에 대한 자세한 내용은 시각적 상태에 대한 Storyboarded 애니메이션을 참조하세요.

도구를 사용하여 테마 작업을 쉽게 작업

컨트롤에 테마를 적용하는 빠른 방법은 Microsoft Visual Studio 문서 개요에서 컨트롤을 마우스 오른쪽 버튼으로 클릭하고 테마 편집 또는 스타일 편집을 선택하는 것입니다(마우스 오른쪽 버튼으로 클릭하는 컨트롤에 따라 다름). 그런 다음 Apply Resource를 선택하여 기존 테마를 적용하거나 Create Empty를 선택하여 새 스타일을 정의할 수 있습니다.

컨트롤 및 접근성

컨트롤에 대한 새 템플릿을 만들 때 컨트롤의 동작 및 시각적 모양을 변경하는 것 외에도 컨트롤이 자신을 나타내는 방식을 접근성 프레임워크로 변경할 수도 있습니다. Windows 앱은 접근성을 위한 Microsoft UI 자동화 프레임워크를 지원합니다. 모든 기본 컨트롤과 해당 템플릿은 컨트롤의 용도 및 함수에 적합한 일반적인 UI 자동화 컨트롤 형식 및 패턴을 지원합니다. 이러한 컨트롤 형식 및 패턴은 보조 기술과 같은 UI 자동화 클라이언트에 의해 해석되며, 이를 통해 더 큰 액세스 가능한 앱 UI의 일부로 컨트롤에 액세스할 수 있습니다.

기본 제어 로직을 분리하고 UI 자동화 아키텍처 요구 사항 중 일부를 충족하기 위해 컨트롤 클래스에는 별도의 클래스인 자동화 피어의 접근성 지원이 포함됩니다. 피어는 템플릿에 명명된 특정 부분이 존재할 것으로 예상하므로 자동화 피어는 컨트롤 템플릿과 상호 작용할 수 있으므로 보조 기술에서 버튼 동작을 호출할 수 있도록 하는 등의 기능을 사용할 수 있습니다.

완전히 새로운 사용자 지정 컨트롤을 만들 때 새 자동화 피어를 만들어 함께 사용할 수도 있습니다. 자세한 내용은 사용자 지정 자동화 피어를 참조하세요.

컨트롤의 기본 템플릿에 대해 자세히 알아보기

XAML 컨트롤에 대한 스타일 및 템플릿을 문서화하는 항목에서는 이전에 설명한 테마 편집 또는 스타일 편집 기술을 사용했는지 확인할 수 있는 동일한 시작 XAML의 발췌 내용을 보여 줍니다. 각 항목에는 시각적 상태의 이름, 사용된 테마 리소스 및 템플릿이 포함된 스타일에 대한 전체 XAML이 나열됩니다. 이 토픽은 템플릿 수정을 이미 시작했으며 원래 템플릿의 모양을 확인하거나 새 템플릿에 필요한 모든 명명된 시각적 상태가 있는지 확인하려는 경우 유용한 지침이 될 수 있습니다.

컨트롤 템플릿의 테마 리소스

XAML 예제의 일부 특성에 대해 {ThemeResource} 마크업 확장을 사용하는 리소스 참조를 발견했을 수 있습니다. 단일 컨트롤 템플릿이 현재 활성 상태인 테마에 따라 다른 값이 될 수 있는 리소스를 사용할 수 있도록 하는 기술입니다. 이는 브러시 및 색에 특히 중요합니다. 테마의 기본 목적은 사용자가 시스템 전체에 어둡게, 밝은 테마 또는 고대비 테마를 적용할지 여부를 선택할 수 있도록 하는 것입니다. XAML 리소스 시스템을 사용하는 앱은 해당 테마에 적합한 리소스 집합을 사용할 수 있으므로 앱의 UI에서 테마 선택은 사용자의 시스템 전체 테마 선택을 반영합니다.

샘플 코드 가져오기