컨트롤의 스타일을 만드는 방법(WPF .NET)

WPF(Windows Presentation Foundation)를 사용하면 재사용 가능한 스타일로 기존 컨트롤의 모양을 사용자 지정할 수 있습니다. 스타일은 앱, 창 및 페이지에 전역적으로 또는 컨트롤에 직접 적용할 수 있습니다.

중요

.NET 7 및 .NET 6에 관한 데스크톱 가이드 설명서는 제작 중입니다.

스타일 만들기

Style를 하나 이상의 요소에 속성 값 집합을 적용하는 편리한 방법으로 생각할 수 있습니다. FrameworkElement 또는 FrameworkContentElement에서 파생되는 모든 요소(예: Window 또는 Button)에 스타일을 사용할 수 있습니다.

스타일을 선언하는 가장 일반적인 방법은 XAML 파일의 Resources 섹션에 있는 리소스입니다. 스타일은 리소스이므로 모든 리소스에 적용되는 동일한 범위 지정 규칙을 따릅니다. 간단히 말해서 스타일을 선언하는 경우 스타일을 적용할 수 있는 위치에 영향을 줍니다. 예를 들어 스타일을 앱 정의 XAML 파일의 루트 요소에서 선언하면 앱의 모든 곳에서 스타일을 사용할 수 있습니다.

<Application x:Class="IntroToStylingAndTemplating.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:IntroToStylingAndTemplating"
             StartupUri="WindowExplicitStyle.xaml">
    <Application.Resources>
        <ResourceDictionary>
            
            <Style x:Key="Header1" TargetType="TextBlock">
                <Setter Property="FontSize" Value="15" />
                <Setter Property="FontWeight" Value="ExtraBold" />
            </Style>
            
        </ResourceDictionary>
    </Application.Resources>
</Application>

앱의 XAML 파일 중 하나에서 스타일을 선언하는 경우 해당 XAML 파일에만 스타일을 사용할 수 있습니다. 리소스 범위 지정 규칙에 대한 자세한 내용은 XAML 리소스 개요를 참조하세요.

<Window x:Class="IntroToStylingAndTemplating.WindowSingleResource"
        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="WindowSingleResource" Height="450" Width="800">
    <Window.Resources>
        
        <Style x:Key="Header1" TargetType="TextBlock">
            <Setter Property="FontSize" Value="15" />
            <Setter Property="FontWeight" Value="ExtraBold" />
        </Style>
        
    </Window.Resources>
    <Grid />
</Window>

스타일은 스타일이 적용되는 요소에 속성을 설정하는 <Setter> 자식 요소로 구성됩니다. 위의 예제에서는 TargetType 특성을 통해 TextBlock 형식에 적용되도록 스타일이 설정되어 습니다. 이 스타일은 FontSize15로 설정하고 FontWeightExtraBold로 설정합니다. 스타일이 변경하는 각 속성에 대해 <Setter>를 추가합니다.

암시적으로 스타일 적용

Style은 여러 요소에 속성 값 집합을 적용하는 편리한 방법입니다. 예를 들어 창에서 다음 TextBlock 요소와 기본 모양을 생각해 보겠습니다.

<StackPanel>
    <TextBlock>My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

스타일 지정 샘플 스크린샷 이전

TextBlock 요소에서 직접 FontSizeFontFamily 같은 속성을 설정하여 기본 모양을 변경할 수 있습니다. 그러나 TextBlock 요소가 일부 속성을 공유하도록 하려면 여기에 나와 있는 것처럼 XAML 파일의 Resources 섹션에서 Style을 만들 수 있습니다.

<Window.Resources>
    <!--A Style that affects all TextBlocks-->
    <Style TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
</Window.Resources>

스타일의 TargetTypeTextBlock 형식으로 설정하고 x:Key 특성을 생략하면 스타일은 해당 스타일로 범위가 지정된 모든 TextBlock 요소에 적용됩니다. 이 범위는 일반적으로 XAML 파일 자체입니다.

이제 TextBlock 요소가 다음과 같이 표시됩니다.

스타일 지정 샘플 스크린샷 기본 스타일

명시적으로 스타일 적용

값을 포함하는 x:Key 특성을 스타일에 추가하면 스타일은 더 이상 TargetType의 모든 요소에 암시적으로 적용되지 않습니다. 스타일을 명시적으로 참조하는 요소에만 스타일이 적용됩니다.

다음은 이전 섹션의 스타일이지만 x:Key 특성을 사용하여 선언되었습니다.

<Window.Resources>
    <Style x:Key="TitleText" TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
</Window.Resources>

스타일을 적용하려면 아래와 같이 StaticResource 태그 확장을 사용하여 요소의 Style 속성을 x:Key 값으로 설정합니다.

<StackPanel>
    <TextBlock Style="{StaticResource TitleText}">My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

첫 번째 TextBlock 요소는 두 번째 TextBlock 요소가 변경되지 않은 상태에서 스타일을 적용합니다. 이전 섹션의 암시적 스타일이 x:Key 특성을 선언하는 스타일로 변경되었습니다. 즉, 스타일의 영향을 받는 유일한 요소는 스타일을 직접 참조한 요소입니다.

스타일 지정 샘플 스크린샷 textblock

스타일은 명시적으로 또는 암시적으로 적용한 후에는 봉인되며 변경할 수 없습니다. 적용된 스타일을 변경하려면 새 스타일을 만들어 기존 스타일을 바꿉니다. 자세한 내용은 IsSealed 속성을 참조하세요.

사용자 지정 논리에 따라 적용할 스타일을 선택하는 개체를 만들 수 있습니다. 예제는 StyleSelector 클래스에 제공된 예제를 참조하세요.

프로그래밍 방식으로 스타일 적용

명명된 스타일을 요소에 프로그래밍 방식으로 할당하려면 리소스 컬렉션에서 스타일을 가져오고 요소의 Style 속성에 할당합니다. 리소스 컬렉션의 항목은 Object 형식입니다. 따라서 Style 속성에 할당하기 전에 검색된 스타일을 System.Windows.Style에 캐스팅해야 합니다. 예를 들어 다음 코드는 textblock1이라는 TextBlock의 스타일을 정의된 스타일 TitleText로 설정합니다.

textblock1.Style = (Style)Resources["TitleText"];
textblock1.Style = CType(Resources("TitleText"), Windows.Style)

스타일 확장

예를 들어 두 개의 TextBlock 요소에서 FontFamily 및 가운데 맞춤 HorizontalAlignment 같은 일부 속성 값을 공유하도록 하려고 합니다. 그러나 텍스트 내 그림도 일부 추가 속성을 포함해야 합니다. 이 작업을 수행하려면 다음과 같이 첫 번째 스타일을 기반으로 새 스타일을 만듭니다.

<Window.Resources>
    <!-- .... other resources .... -->

    <!--A Style that affects all TextBlocks-->
    <Style TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
    
    <!--A Style that extends the previous TextBlock Style with an x:Key of TitleText-->
    <Style BasedOn="{StaticResource {x:Type TextBlock}}"
           TargetType="TextBlock"
           x:Key="TitleText">
        <Setter Property="FontSize" Value="26"/>
        <Setter Property="Foreground">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Offset="0.0" Color="#90DDDD" />
                        <GradientStop Offset="1.0" Color="#5BFFFF" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<StackPanel>
    <TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

TextBlock 스타일은 이제 가운데 맞춤되고 26 크기의 Comic Sans MS 글꼴을 사용하며 전경색은 예제에 표시된 LinearGradientBrush로 설정됩니다. 기본 스타일의 FontSize 값은 재정의됩니다. Style의 동일한 속성을 가리키는 Setter가 두 개 이상 있는 경우 마지막으로 선언된 Setter가 우선적으로 적용됩니다.

TextBlock 요소는 이제 다음과 같이 표시됩니다.

스타일 적용된 TextBlock

TitleText 스타일은 BasedOn="{StaticResource {x:Type TextBlock}}"으로 참조되는 TextBlock 형식에 대해 만들어진 스타일을 확장합니다. 스타일의 x:Key를 사용하여 x:Key가 포함된 스타일을 확장할 수도 있습니다. 예를 들어 Header1이라는 스타일을 확장하려는 경우 BasedOn="{StaticResource Header1}"를 사용합니다.

TargetType 속성과 x:Key 특성의 관계

앞서 설명한 것처럼 x:Key 스타일을 할당하지 않고 TargetType 속성을 TextBlock로 설정하면 스타일이 모든 TextBlock 요소에 적용됩니다. 이 경우 x:Key는 암시적으로 {x:Type TextBlock}으로 설정됩니다. 즉, x:Key 값을 {x:Type TextBlock} 이외의 값으로 명시적으로 설정하면 Style이 자동으로 모든 TextBlock 요소에 적용되지 않습니다. 대신 x:Key 값을 사용하여 TextBlock 요소에 스타일을 명시적으로 적용해야 합니다. 스타일이 리소스 섹션에 있고 스타일에 TargetType 속성을 설정하지 않은 경우 x:Key 특성을 설정해야 합니다.

TargetType 속성은 x:Key의 기본값을 제공할 뿐 아니라 setter 속성이 적용되는 형식을 지정합니다. TargetType을 지정하지 않으면 구문 Property="ClassName.Property"를 사용하여 클래스 이름으로 Setter 개체의 속성을 정규화해야 합니다. 예를 들어 Property="FontSize"를 설정하는 대신 Property"TextBlock.FontSize" 또는 "Control.FontSize"로 설정해야 합니다.

또한 WPF 컨트롤은 대부분 다른 WPF 컨트롤의 조합으로 구성된다는 점을 알아야 합니다. 한 형식의 모든 컨트롤에 적용되는 스타일을 만들 경우 예기치 않은 결과가 발생할 수 있습니다. 예를 들어 Window에서 TextBlock 형식을 대상으로 하는 스타일을 만드는 경우 TextBlockListBox 같은 다른 컨트롤의 일부인 경우에도 해당 스타일이 창의 모든 TextBlock 컨트롤에 적용됩니다.

참조