컨트롤에 대한 템플릿을 만드는 방법(WPF.NET)

WPF(Windows Presentation Foundation)를 사용하면 다시 사용할 수 있는 템플릿을 사용하여 기존 컨트롤의 시각적 구조 및 동작을 사용자 지정할 수 있습니다. 템플릿은 애플리케이션, 창 및 페이지에 전역적으로 또는 컨트롤에 직접 적용할 수 있습니다. 새 컨트롤을 만들어야 하는 대부분의 시나리오는 이 방법 대신 기존 컨트롤의 새 템플릿을 만드는 방법으로 해결할 수 있습니다.

이 문서에서는 Button 컨트롤을 위한 새 ControlTemplate 만들기를 탐색합니다.

ControlTemplate을 만들어야 하는 경우

컨트롤에는 Background, Foreground, FontFamily 등의 여러 속성이 있습니다. 이러한 속성은 컨트롤의 여러 모양을 제어하지만, 이러한 속성을 설정하여 변경할 수 있는 부분은 제한되어 있습니다. 예를 들어 Foreground에서 FontStyle 속성을 파란색으로 설정하고 CheckBox을 기울임꼴로 설정할 수 있습니다. 컨트롤의 모양을 컨트롤의 다른 속성에서 할 수 있는 이상으로 사용자 지정하려면 ControlTemplate을 만듭니다.

대부분의 사용자 인터페이스에서 단추의 모양은 일반적으로 약간의 텍스트가 있는 사각형으로 동일합니다. 둥근 단추를 만들려면 단추에서 상속하는 새 컨트롤을 만들거나 단추의 기능을 다시 만들 수 있습니다. 또한 새 사용자 컨트롤은 원형 그래픽을 제공합니다.

기존 컨트롤의 시각적 레이아웃을 사용자 지정하면 새 컨트롤을 만들지 않아도 됩니다. 둥근 단추의 경우 원하는 시각적 레이아웃을 사용하여 ControlTemplate 만듭니다.

반면에 새 기능, 다른 속성 및 새 설정이 있는 컨트롤이 필요한 경우 새 UserControl설정을 만듭니다.

필수 조건

새 WPF 애플리케이션을 만듭니다. MainWindow.xaml(또는 원하는 다른 창)에서 Window< 요소에서> 다음 속성을 설정합니다.

재산 가치
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

<Window> 요소의 콘텐츠를 다음 XAML로 설정합니다.

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

마지막에 MainWindow.xaml 파일은 다음 XAML과 유사하게 표시됩니다.

<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>

애플리케이션을 실행하는 경우 다음 이미지와 같습니다.

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

ControlTemplate 만들기

ControlTemplate을 선언하는 가장 일반적인 방법은 XAML 파일에 있는 Resources 섹션의 리소스입니다. 템플릿은 리소스이므로 모든 리소스와 동일한 범위 지정 규칙을 따릅니다. 템플릿을 선언하는 위치는 템플릿을 적용할 수 있는 위치에 영향을 줍니다. 예를 들어 애플리케이션 정의 XAML 파일의 루트 요소에서 템플릿을 선언하는 경우 애플리케이션의 아무 곳이나 템플릿을 사용할 수 있습니다. 창에서 템플릿을 정의하면 해당 창의 컨트롤만 템플릿을 사용할 수 있습니다.

시작하려면 Window.Resources 파일에 요소를 추가 합니다.

<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> 을 만들고 다음 속성을 설정합니다.

재산 가치
x:Key roundbutton
TargetType Button

이 컨트롤 템플릿은 간단합니다.

  • 컨트롤의 루트 요소인 Grid
  • 버튼의 둥근 외관을 그리는 Ellipse
  • 사용자가 지정한 단추 콘텐츠를 표시하는 ContentPresenter
<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

템플릿 바인딩

ControlTemplate속성을 만들 때 공용 속성을 사용하여 컨트롤의 모양을 변경하는 것이 좋습니다. TemplateBinding 태그 확장은 컨트롤이 정의하는 public 속성에 있는 ControlTemplate 요소의 속성을 바인딩합니다. TemplateBinding을 사용하면 컨트롤의 속성이 템플릿의 매개 변수로 작동합니다. 컨트롤에 속성을 설정하면 TemplateBinding이 있는 요소에 값이 전달됩니다.

타원

< > 및 Stroke 속성은 컨트롤 ForegroundBackground 속성에 바인딩됩니다.

콘텐츠 프리젠터

템플릿에는 ContentPresenter< 요소도 포함됩니다>. 이 템플릿은 단추용으로 설계되었기 때문에, 단추가 ContentControl에서 상속된다는 것을 기억하십시오. 단추는 요소의 내용을 표시합니다. 일반 텍스트 또는 다른 컨트롤과 같이 단추 내의 모든 항목을 설정할 수 있습니다. 다음 두 예제는 모두 유효한 단추입니다.

<Button>My Text</Button>

<!-- and -->

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

위의 두 예제에서 텍스트와 확인란은 Button.Content 속성으로 설정됩니다. 콘텐츠로 설정된 것은 그 무엇이든 <ContentPresenter>를 통해 표시할 수 있으며, 이것이 바로 템플릿이 하는 일입니다.

형식 ControlTemplateContentControl 유형, 예를 들어 Button에 적용할 경우, 템플릿은 요소 트리에서 ContentPresenter을(를) 찾습니다. 만약 ContentPresenter를 찾으면 템플릿은 컨트롤의 Content 속성을 ContentPresenter에 자동으로 바인딩합니다.

템플릿 사용

이 문서의 시작 부분에 선언한 단추를 찾습니다.

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

두 번째 단추의 Template 속성을 roundbutton 리소스로 설정합니다.

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

프로젝트를 실행하고 결과를 살펴보면 단추의 배경이 둥근 것을 볼 수 있습니다.

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

단추가 원형이 아니라 비스듬하게 기울어진 것을 알 수 있습니다. <Ellipse> 요소는 그 작동 방식 때문에 항상 사용 가능한 공간을 채우도록 확장됩니다. 단추의 widthheight 속성을 동일한 값으로 변경하여 원을 균일하게 만듭니다.

<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 창

트리거 추가

템플릿이 적용된 단추가 다르게 보이더라도 다른 단추와 동일하게 작동합니다. 단추를 누르면 Click 이벤트가 발생합니다. 그러나 단추 위로 마우스를 이동하면 단추의 시각적 개체가 변경되지 않는다는 것을 알 수 있습니다. 템플릿은 이러한 시각적 상호 작용을 정의합니다.

WPF에서 제공하는 동적 이벤트 및 속성 시스템을 사용하여 값에 대한 특정 속성을 보고 적절한 경우 템플릿을 다시 설정할 수 있습니다. 이 예제에서는 단추의 IsMouseOver 속성을 확인합니다. 마우스가 컨트롤 위에 있을 때 <Ellipse> 스타일을 새로운 색으로 지정합니다. 이러한 유형의 트리거를 PropertyTrigger라고 합니다.

이 기능이 작동하려면 참조할 수 있는 <Ellipse>에 이름을 추가해야 합니다. backgroundElement라는 이름을 지정합니다.

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

다음으로, ControlTemplate.Triggers 컬렉션에 새 Trigger를 추가합니다. 트리거는 IsMouseOver 이벤트가 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>

다음으로 <Trigger><Setter>를 추가하여 <Ellipse>Fill 속성을 새로운 색으로 변경합니다.

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

프로젝트를 실행합니다. 버튼 위로 마우스를 이동하면 <타원>의 색이 변경됩니다.

마우스가 WPF 버튼 위로 이동하면 채우기 색상이 변경됨

VisualState 사용

시각적 상태는 컨트롤을 통해 정의하고 트리거됩니다. 예를 들어 컨트롤 위로 마우스를 이동하면 컨트롤이 CommonStates.MouseOver 상태를 트리거합니다. 컨트롤의 현재 상태에 따라 속성 변경에 애니메이션 효과를 적용할 수 있습니다. 이전 섹션에서 <PropertyTrigger>를 사용하여 버튼의 배경을 IsMouseOver 속성이 true일 때 AliceBlue으로 변경했습니다. 여기서는 그 대신 색의 변화에 애니메이션 효과를 적용하는 시각적 상태를 만들어 부드럽게 전환하겠습니다. VisualStates에 대한 자세한 내용은 WPF의 스타일 및 템플릿을 참조하세요.

PropertyTrigger<를> 애니메이션 시각적 상태로 변환하려면 템플릿에서 ControlTemplate.Triggers< 요소를 제거>합니다.

<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> 루트에 <VisualStateManager.VisualStateGroups> 요소를 추가하고, <VisualStateGroup>CommonStates을(를) 포함시킵니다. 두 가지 상태 NormalMouseOver를 정의합니다.

<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<에서> 정의한 애니메이션을 적용합니다. 각 상태에 대한 애니메이션을 만듭니다. Storyboard 요소 내부에 애니메이션을 <배치합니다.> 스토리보드에 대한 자세한 내용은 스토리보드 개요를 참조하세요.

  • 정상

    이 상태는 타원 채우기에 애니메이션 효과를 적용하여 컨트롤의 Background 색으로 복원합니다.

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

    이 상태는 타원 Background 색에 애니메이션 효과를 적용하여 새로운 Yellow 색으로 변경합니다.

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

이제 ControlTemplate<은> 다음 코드와 같이 표시됩니다.

<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>

프로젝트를 실행합니다. 단추 위로 마우스를 이동하면 타<원의 색이> 애니메이션 효과를 줍니다.

마우스를 WPF 버튼 위로 이동하면 시각적 상태와 함께 채우기 색이 변합니다

다음 단계