컨트롤의 템플릿 만들기Create a template for a control

WPF(Windows Presentation Foundation)를 사용하면 다시 사용할 수 있는 템플릿을 사용하여 기존 컨트롤의 시각적 구조 및 동작을 사용자 지정할 수 있습니다.With Windows Presentation Foundation (WPF), you can customize an existing control's visual structure and behavior with your own reusable template. 템플릿은 애플리케이션, 창 및 페이지에 전역적으로 또는 컨트롤에 직접 적용할 수 있습니다.Templates can be applied globally to your application, windows and pages, or directly to controls. 새 컨트롤을 만들어야 하는 대부분의 시나리오는 이 방법 대신 기존 컨트롤의 새 템플릿을 만드는 방법으로 해결할 수 있습니다.Most scenarios that require you to create a new control can be covered by instead creating a new template for an existing control.

중요

.NET 5(및 .NET Core)에 관한 데스크톱 가이드 설명서는 제작 중입니다.The Desktop Guide documentation for .NET 5 (and .NET Core) is under construction.

이 문서에서는 Button 컨트롤의 새 ControlTemplate을 만드는 방법을 살펴봅니다.In this article, you'll explore creating a new ControlTemplate for the Button control.

ControlTemplate을 만들어야 하는 경우When to create a ControlTemplate

컨트롤에는 Background, Foreground, FontFamily 등의 여러 속성이 있습니다.Controls have many properties, such as Background, Foreground, and FontFamily. 이러한 속성은 컨트롤의 여러 모양을 제어하지만, 이러한 속성을 설정하여 변경할 수 있는 부분은 제한되어 있습니다.These properties control different aspects of the control's appearance, but the changes that you can make by setting these properties are limited. 예를 들어 CheckBox에서 Foreground 속성을 파란색으로 설정하고 FontStyle을 기울임꼴로 설정할 수 있습니다.For example, you can set the Foreground property to blue and FontStyle to italic on a CheckBox. 컨트롤의 모양을 컨트롤의 다른 속성에서 할 수 있는 이상으로 사용자 지정하려면 ControlTemplate을 만듭니다.When you want to customize the control's appearance beyond what setting the other properties on the control can do, you create a ControlTemplate.

대부분의 사용자 인터페이스에서 단추의 모양은 일반적으로 약간의 텍스트가 있는 사각형으로 동일합니다.In most user interfaces, a button has the same general appearance: a rectangle with some text. 모서리가 둥근 단추를 만들려면 단추에서 상속하는 새 컨트롤을 만들거나 단추의 기능을 다시 만들면 됩니다.If you wanted to create a rounded button, you could create a new control that inherits from the button or recreates the functionality of the button. 또한 새로운 사용자 컨트롤은 원형 시각적 개체를 제공합니다.In addition, the new user control would provide the circular visual.

기존 컨트롤의 시각적 레이아웃을 사용자 지정하면 새 컨트롤을 만들지 않아도 됩니다.You can avoid creating new controls by customizing the visual layout of an existing control. 모서리가 둥근 단추를 사용하여 원하는 시각적 레이아웃으로 ControlTemplate을 만듭니다.With a rounded button, you create a ControlTemplate with the desired visual layout.

반면, 새 기능, 다른 속성, 새 설정이 포함된 컨트롤이 필요한 경우 새 UserControl을 만듭니다.On the other hand, if you need a control with new functionality, different properties, and new settings, you would create a new UserControl.

사전 요구 사항Prerequisites

새 WPF 애플리케이션을 만든 다음, MainWindow.xaml(또는 원하는 다른 창)에서 <Window> 요소의 다음 속성을 설정합니다.Create a new WPF application and in MainWindow.xaml (or another window of your choice) set the following properties on the <Window> element:

Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

<Window> 요소의 콘텐츠를 다음 XAML로 설정합니다.Set the content of the <Window> element to the following XAML:

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
</StackPanel>

최종적으로 MainWindow.xaml 파일의 모양은 다음과 비슷합니다.In the end, the MainWindow.xaml file should look similar to the following:

<Window x:Class="IntroToStylingAndTemplating.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:IntroToStylingAndTemplating"
        mc:Ignorable="d"
        Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
    <StackPanel Margin="10">
        <Label>Unstyled Button</Label>
        <Button>Button 1</Button>
        <Label>Rounded Button</Label>
        <Button>Button 2</Button>
    </StackPanel>
</Window>

애플리케이션을 실행하면 다음과 비슷한 모양입니다.If you run the application, it looks like the following:

스타일이 지정되지 않은 두 개의 단추가 있는 WPF 창

ControlTemplate 만들기Create a ControlTemplate

ControlTemplate을 선언하는 가장 일반적인 방법은 XAML 파일에 있는 Resources 섹션의 리소스입니다.The most common way to declare a ControlTemplate is as a resource in the Resources section in a XAML file. 템플릿은 리소스이므로 모든 리소스에 적용되는 동일한 범위 지정 규칙을 따릅니다.Because templates are resources, they obey the same scoping rules that apply to all resources. 간단히 말해서, 템플릿을 선언하는 위치는 템플릿을 적용할 수 있는 위치에 영향을 줍니다.Put simply, where you declare a template affects where the template can be applied. 예를 들어 애플리케이션 정의 XAML 파일의 루트 요소에서 컨트롤을 선언하면 애플리케이션의 모든 위치에서 템플릿을 사용할 수 있습니다.For example, if you declare the template in the root element of your application definition XAML file, the template can be used anywhere in your application. 창에서 템플릿을 정의하면 해당 창의 컨트롤만 템플릿을 사용할 수 있습니다.If you define the template in a window, only the controls in that window can use the template.

먼저 Window.Resources 요소를 MainWindow.xaml 파일에 추가합니다.To start with, add a Window.Resources element to your MainWindow.xaml file:

<Window x:Class="IntroToStylingAndTemplating.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:IntroToStylingAndTemplating"
        mc:Ignorable="d"
        Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
    <Window.Resources>
        
    </Window.Resources>
    <StackPanel Margin="10">
        <Label>Unstyled Button</Label>
        <Button>Button 1</Button>
        <Label>Rounded Button</Label>
        <Button>Button 2</Button>
    </StackPanel>
</Window>

다음 속성을 설정하여 새 <ControlTemplate> 을 만듭니다.Create a new <ControlTemplate> with the following properties set:

x:Keyx:Key roundbutton
TargetType Button

이 컨트롤 템플릿은 간단하게 다음 중 하나가 됩니다.This control template will be simple:

  • 컨트롤의 루트 요소인 Grida root element for the control, a Grid
  • 단추의 둥근 모서리 모양을 그리는 Ellipsean Ellipse to draw the rounded appearance of the button
  • 사용자 지정 단추 콘텐츠를 표시하는 ContentPresentera ContentPresenter to display the user-specified button content
<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

TemplateBindingTemplateBinding

ControlTemplate을 만들 때에도 여전히 public 속성을 사용하여 컨트롤의 모양을 변경할 수 있습니다.When you create a new ControlTemplate, you still might want to use the public properties to change the control's appearance. TemplateBinding 태그 확장은 ControlTemplate에 있는 요소의 속성을 컨트롤이 정의하는 public 속성에 바인딩합니다.The TemplateBinding markup extension binds a property of an element that is in the ControlTemplate to a public property that is defined by the control. TemplateBinding을 사용하면 컨트롤의 속성이 템플릿의 매개 변수로 작동합니다.When you use a TemplateBinding, you enable properties on the control to act as parameters to the template. 즉, 컨트롤의 속성을 설정하면 해당 값은 TemplateBinding이 있는 요소로 전달됩니다.That is, when a property on a control is set, that value is passed on to the element that has the TemplateBinding on it.

타원Ellipse

<Ellipse> 요소의 FillStroke 속성은 컨트롤의 ForegroundBackground 속성에 바인딩됩니다.Notice that the Fill and Stroke properties of the <Ellipse> element are bound to the control's Foreground and Background properties.

ContentPresenterContentPresenter

<ContentPresenter> 요소도 템플릿에 추가됩니다.A <ContentPresenter> element is also added to the template. 이 템플릿은 단추용으로 디자인되었으므로 단추가 ContentControl에서 상속한다는 점을 고려해야 합니다.Because this template is designed for a button, take into consideration that the button inherits from ContentControl. 단추는 요소의 콘텐츠를 표시합니다.The button presents the content of the element. 단추 내에 일반 텍스트, 다른 컨트롤 등의 모든 것을 설정할 수 있습니다.You can set anything inside of the button, such as plain text or even another control. 다음 두 단추 모두 유효한 단추입니다.Both of the following are valid buttons:

<Button>My Text</Button>

<!-- and -->

<Button>
    <CheckBox>Checkbox in a button</CheckBox>
</Button>

위의 두 예제에서 텍스트와 확인란은 Button.Content 속성으로 설정됩니다.In both of the previous examples, the text and the checkbox are set as the Button.Content property. 콘텐츠로 설정된 모든 항목은 <ContentPresenter> 를 통해 표시할 수 있으며, 이것이 바로 템플릿이 하는 일입니다.Whatever is set as the content can be presented through a <ContentPresenter>, which is what the template does.

ControlTemplateButton 같은 ContentControl 형식에 적용되면 요소 트리에서 ContentPresenter가 검색됩니다.If the ControlTemplate is applied to a ContentControl type, such as a Button, a ContentPresenter is searched for in the element tree. ContentPresenter가 발견되면 템플릿에서 자동으로 컨트롤의 Content 속성을 ContentPresenter에 바인딩합니다.If the ContentPresenter is found, the template automatically binds the control's Content property to the ContentPresenter.

템플릿 사용Use the template

이 문서의 시작 부분에서 선언한 단추를 찾습니다.Find the buttons that were declared at the start of this article.

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
</StackPanel>

두 번째 단추의 Template 속성을 roundbutton 리소스로 설정합니다.Set the second button's Template property to the roundbutton resource:

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button Template="{StaticResource roundbutton}">Button 2</Button>
</StackPanel>

프로젝트를 실행하고 결과를 살펴보면 단추의 배경이 둥근 것을 볼 수 있습니다.If you run the project and look at the result, you'll see that the button has a rounded background.

템플릿 타원 단추가 하나 있는 WPF 창

단추가 원이 아니라 타원인 것을 볼 수 있습니다.You may have noticed that the button isn't a circle but is skewed. <Ellipse> 요소는 작동 방식 때문에 항상 사용 가능한 공간을 채우도록 확장됩니다.Because of the way the <Ellipse> element works, it always expands to fill the available space. 단추의 widthheight 속성을 동일한 값으로 변경하여 원을 균일하게 만듭니다.Make the circle uniform by changing the button's width and height properties to the same value:

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button Template="{StaticResource roundbutton}" Width="65" Height="65">Button 2</Button>
</StackPanel>

템플릿 원형 단추가 하나 있는 WPF 창

트리거 추가Add a Trigger

템플릿이 적용된 단추가 다르게 보이더라도 다른 단추와 동일하게 작동합니다.Even though a button with a template applied looks different, it behaves the same as any other button. 단추를 누르면 Click 이벤트가 발생합니다.If you press the button, the Click event fires. 그러나 마우스를 단추 위로 이동하면 단추의 시각적 개체가 변하지 않는 것을 볼 수 있습니다.However, you may have noticed that when you move your mouse over the button, the button's visuals don't change. 이러한 시각적 개체 상호 작용은 모두 템플릿에서 정의합니다.These visual interactions are all defined by the template.

WPF에서 제공하는 동적 이벤트 및 속성 시스템을 사용하면 값의 특정 속성을 살펴본 후 적시에 템플릿 스타일을 다시 지정할 수 있습니다.With the dynamic event and property systems that WPF provides, you can watch a specific property for a value and then restyle the template when appropriate. 이 예제에서는 단추의 IsMouseOver 속성을 살펴보겠습니다.In this example, you'll watch the button's IsMouseOver property. 마우스가 컨트롤 위에 있을 때 <Ellipse> 스타일을 새로운 색으로 지정합니다.When the mouse is over the control, style the <Ellipse> with a new color. 이러한 유형의 트리거를 PropertyTrigger 라고 합니다.This type of trigger is known as a PropertyTrigger.

이 방법을 사용하려면 참조할 수 있는 이름을 <Ellipse> 에 추가해야 합니다.For this to work, you'll need to add a name to the <Ellipse> that you can reference. backgroundElement 라는 이름을 지정합니다.Give it the name of backgroundElement.

<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />

다음으로, ControlTemplate.Triggers 컬렉션에 새 Trigger를 추가합니다.Next, add a new Trigger to the ControlTemplate.Triggers collection. 이 트리거는 IsMouseOver 이벤트의 true 값을 감시합니다.The trigger will watch the IsMouseOver event for the value true.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="true">

        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

다음으로, <Ellipse>Fill 속성을 새로운 색으로 변경하는 <Setter><Trigger> 에 추가합니다.Next, add a <Setter> to the <Trigger> that changes the Fill property of the <Ellipse> to a new color.

<Trigger Property="IsMouseOver" Value="true">
    <Setter Property="Fill" TargetName="backgroundElement" Value="AliceBlue"/>
</Trigger>

프로젝트를 실행합니다.Run the project. 마우스를 단추 위로 이동하면 <Ellipse> 의 색이 변경됩니다.Notice that when you move the mouse over the button, the color of the <Ellipse> changes.

마우스를 WPF 단추 위로 이동하면 채우기 색이 변함

VisualState 사용Use a VisualState

시각적 상태는 컨트롤을 통해 정의하고 트리거됩니다.Visual states are defined and triggered by a control. 예를 들어 마우스를 컨트롤 위로 이동하면 CommonStates.MouseOver 상태가 트리거됩니다.For example, when the mouse is moved on top of the control, the CommonStates.MouseOver state is triggered. 컨트롤의 현재 상태에 따라 속성 변경에 애니메이션 효과를 적용할 수 있습니다.You can animate property changes based on the current state of the control. 이전 섹션에서는 <PropertyTrigger> 를 사용하여 IsMouseOver 속성이 true일 때 단추의 전경을 AliceBlue로 변경했습니다.In the previous section, a <PropertyTrigger> was used to change the foreground of the button to AliceBlue when the IsMouseOver property was true. 여기서는 그 대신 색의 변화에 애니메이션 효과를 적용하는 시각적 상태를 만들어 부드럽게 전환하겠습니다.Instead, create a visual state that animates the change of this color, providing a smooth transition. VisualStates 에 대한 자세한 내용은 WPF의 스타일 및 템플릿을 참조하세요.For more information about VisualStates, see Styles and templates in WPF.

<PropertyTrigger> 를 애니메이션 시각적 상태로 변환하려면 먼저 템플릿에서 <ControlTemplate.Triggers> 요소를 제거합니다.To convert the <PropertyTrigger> to an animated visual state, First, remove the <ControlTemplate.Triggers> element from your template.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

다음으로, 컨트롤 템플릿의 <Grid> 루트에 CommonStates<VisualStateGroup> 중 하나를 사용하여 <VisualStateManager.VisualStateGroups> 요소를 추가합니다.Next, in the <Grid> root of the control template, add the <VisualStateManager.VisualStateGroups> element with a <VisualStateGroup> for CommonStates. 두 가지 상태 NormalMouseOver를 정의합니다.Define two states, Normal and MouseOver.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                </VisualState>
                <VisualState Name="MouseOver">
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

<VisualState> 에 정의된 애니메이션은 이 상태가 트리거될 때 적용됩니다.Any animations defined in a <VisualState> are applied when that state is triggered. 각 상태에 대한 애니메이션을 만듭니다.Create animations for each state. 애니메이션은 <Storyboard> 요소 내에 배치됩니다.Animations are put inside of a <Storyboard> element. 스토리보드에 대한 자세한 내용은 스토리보드 개요를 참조하세요.For more information about storyboards, see Storyboards Overview.

  • 보통Normal

    이 상태는 타원 채우기에 애니메이션 효과를 적용하여 컨트롤의 Background 색으로 복원합니다.This state animates the ellipse fill, restoring it to the control's Background color.

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
            To="{TemplateBinding Background}"
            Duration="0:0:0.3"/>
    </Storyboard>
    
  • MouseOverMouseOver

    이 상태는 타원 Background 색에 애니메이션 효과를 적용하여 새로운 Yellow 색으로 변경합니다.This state animates the ellipse Background color to a new color: Yellow.

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" 
            To="Yellow" 
            Duration="0:0:0.3"/>
    </Storyboard>
    

<ControlTemplate> 은 이제 다음과 같이 표시됩니다.The <ControlTemplate> should now look like the following.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="backgroundElement" 
                            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                            To="{TemplateBinding Background}"
                            Duration="0:0:0.3"/>
                    </Storyboard>
                </VisualState>
                <VisualState Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="backgroundElement" 
                            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" 
                            To="Yellow" 
                            Duration="0:0:0.3"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

프로젝트를 실행합니다.Run the project. 마우스를 단추 위로 이동하면 <Ellipse> 의 색에 애니메이션 효과가 적용됩니다.Notice that when you move the mouse over the button, the color of the <Ellipse> animates.

마우스를 WPF 단추 위로 이동하면 채우기 색이 변함

다음 단계Next steps