티스토리 뷰

반응형

2022.07.13 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part7 Behavior

2022.07.05 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part6 Command

2022.06.27 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part5 Converter

2022.06.15 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part4-2 Data Binding

2022.06.13 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part4-1 Data Binding

2022.06.07 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-3

2022.06.02 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-2

2022.05.30 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-1

2022.05.11 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part2

2022.05.09 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part1

1. Template

WPF(Xaml base ui)에서는 재사용이 가능한 템플릿(Template)을 사용하여 기존 컨트롤의 시각적 구조와 동작을 사용자가 지정할 수 있습니다. 더 자세한 사항은 여기를 참고하시기 바랍니다.

Control

  • ControlTemplate
  • ItemTemplate
    • ItemsControl을 기본으로 하는 컨트롤들의 아이템 내용을 표시하는데 사용되는 템플릿을 가져오거나 설정하는 프로퍼티입니다.
    • ListBox, ComboBox, ItemsControl, ListView 등의 컨트롤에서 사용할 수 있습니다.
    • 내부에서 Binidng만 사용할 수 있습니다.
  • ItemsPanelTemplate
    • ItemsControl을 기본으로 하는 컨트롤들의 아이템 출력 레이아웃을 가져오거나 설정하는 프로퍼티 입니다. 
    • ListBox, ComboBox, ItemsControl, ListView 등의 컨트롤에서 사용할 수 있습니다.
  • HeaderTemplate
    • HeaderedContentControl의 Header 부분의 내용을 표시하는데 사용되는 템플릿을 가져오거나 설정하는 프로퍼티입니다.
    • TabControl 컨트롤에서 사용할 수 있습니다.
  • ContentTemplate
    • ContentControl의 Content 부분의 내용을 표시하는데 사용되는 템플릿을 가져오거나 설정하는 프로퍼티입니다.
    • ConttentControl 컨트롤에서 사용할 수 있습니다.

Data

  • DataTemplate
    • 단일 데이터 모델을 유연하게 정의할 수 있는 템플릿을 생성할 때 사용합니다.
    • ListBox, ComboBox, ItemsControl, ListView 등의 컨트롤에서 사용할 수 있습니다.
  • HierarchicalDataTemplate
    • 계층적 데이터 모델을 유연하게 정의할 수 있는 템플릿을 생성할 때 사용합니다.
    • TreeView 컨트롤에서 사용할 수 있습니다.

2. 기존 컨트롤의 ControlTemplate 편집하기

Button 컨트롤을 하나 추가한 후 Document Outline에서 마우스 오른쪽 -> Edit Template -> Edit a Copy...

스타일 이름을 RoundButtonStyle로 변경합니다.

아래 리소스가 Button 컨트롤를 구성하는 Xaml 코드 입니다.

        <Style x:Key="FocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
        <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
        <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
        <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
        <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
        <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
        <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
        <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
        <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
        <Style x:Key="RoundButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
            <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
                            <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsDefaulted" Value="true">
                                <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
                                <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

3. ControlTemplate 구조

  • Style에서 사용할 리소스들
  • Style 본체
    • Setter 기본 값
    • Setter Template
      • ControlTemplate 본체
        • Border 컨트롤 메인 테두리
          • ContentPresenter 컨트롤의 메인 컨텐츠
        • Triggers

4. ControlTemplate를 수정해서 컨트롤의 모양을 변경할 때 주의 사항 및 팁

  • x:Name에 PART_로 시작하는 컨트롤들은 Code behind에서 작업을 하기 때문에 삭제하거나 다른 컨트롤로 변경하면 오류가 발생할 수 있습니다.
  • ControlTemplate 내부에는 b:Interaction.Triggers 등을 사용할 수 없습니다.
  • Style 내부에 여러개의 Setter가 있는데, 컨트롤의 기본값을 정의한다고 생각하시면 됩니다.
  • EventTrigger를 사용할 수는 있지만, Storyboard를 실행하는 용도로만 사용이 가능합니다.
  • 내부에서 사용하는 컬러 리소스는 ControlTemplate에서 접근할 수 있는 위치에 있어야 합니다.

5. 컨트롤 모양 수정하기

버튼 테두리에 라운드를 주기 위해서는 Border에 CornerRadius를 입력하면 됩니다.

<Border
    x:Name="border"
    Background="{TemplateBinding Background}"
    BorderBrush="{TemplateBinding BorderBrush}"
    BorderThickness="{TemplateBinding BorderThickness}"
    CornerRadius="10"
    SnapsToDevicePixels="true">

이번에는 버튼 내부에 스마일을 추가하겠습니다.

<Grid HorizontalAlignment="Center">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <TextBlock VerticalAlignment="Center" Text="😊" />
    <ContentPresenter
        x:Name="contentPresenter"
        Grid.Column="1"
        Margin="{TemplateBinding Padding}"
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
        Focusable="False"
        RecognizesAccessKey="True"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>

실행화면

6. ControlTemplate를 잘 활용하려면...

오랜 시간동안 삽질을 해야만 원하는 결과를 얻을 수 있습니다. 

다음 포스팅도 계속 이어가도록 하고, 소스는 마지막에 올리겠습니다. 

 

반응형
댓글
댓글쓰기 폼
반응형
Total
698,835
Today
92
Yesterday
213
«   2022/08   »
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
08-14 22:47
글 보관함