다음을 통해 공유


컨트롤이란?

WPF(Windows Presentation Foundation)는 거의 모든 Windows 앱(예: Button, Label, TextBoxMenu및)에서 사용되는 많은 공통 UI 구성 요소와 ListBox함께 제공됩니다. 지금까지 이러한 개체를 컨트롤이라고 합니다. "control"이라는 용어는 앱에서 표시되는 개체를 나타내는 모든 클래스를 의미하기 위해 느슨하게 사용됩니다. 클래스가 눈에 보이는 존재감을 가지기 위해 Control 클래스를 상속할 필요는 없다는 점을 유의해야 합니다. Control 클래스에서 상속된 클래스에는 ControlTemplate이 포함되어 있습니다. 따라서 컨트롤 소비자는 새 서브클래스를 만들지 않아도 컨트롤의 모양을 대폭 변경할 수 있습니다. 이 문서에서는 WPF에서 일반적으로 사용되는 클래스에서 상속하는 컨트롤과 그렇지 않은 컨트롤의 사용 방법을 논의합니다.

컨트롤의 인스턴스 만들기

XAML(Extensible Application Markup Language) 또는 코드를 사용하여 앱에 컨트롤을 추가할 수 있습니다. 예를 들어 사용자에게 이름과 주소를 요청하는 WPF 창의 다음 이미지를 고려합니다.

이름과 주소에 레이블이 지정된 두 개의 텍스트 상자가 있는 WPF 앱의 스크린샷 두 개의 단추가 표시됩니다. 한 단추의 이름은 '초기화'이고 다른 단추는 '제출'입니다.

이 창에는 레이블 2개, 텍스트 상자 2개, 단추 2개 등 6개의 컨트롤이 있습니다. XAML은 다음 코드 조각에 설명된 대로 이러한 컨트롤을 만드는 데 사용됩니다.

<Window x:Class="Examples.ExampleApp"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Input Record" Height="Auto" Width="300" SizeToContent="Height">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Label>Enter your name:</Label>
        <TextBox Grid.Row="0" Grid.Column="1" Name="FirstName" Margin="2" />

        <Label Grid.Row="1">Enter your address:</Label>
        <TextBox Grid.Row="1" Grid.Column="1" Name="LastName" Margin="2" />

        <Button Grid.Row="2" Grid.Column="0" Name="Reset" Margin="2">Reset</Button>
        <Button Grid.Row="2" Grid.Column="1" Name="Submit" Margin="2">Submit</Button>
    </Grid>
</Window>

모든 컨트롤은 XAML에서 비슷하게 만들 수 있습니다. 코드에서 동일한 창을 만들 수 있습니다.

// Grid container which is the content of the Window
Grid container = new() { Margin = new Thickness(5) };
container.RowDefinitions.Add(new RowDefinition());
container.RowDefinitions.Add(new RowDefinition());
container.RowDefinitions.Add(new RowDefinition());
container.ColumnDefinitions.Add(new ColumnDefinition());
container.ColumnDefinitions.Add(new ColumnDefinition());

// Create the two labels, assign the second label to the second row
Label labelName = new() { Content = "Enter your name:" };
container.Children.Add(labelName);

Label labelAddress = new() { Content = "Enter your address:" };
Grid.SetRow(labelAddress, 1);
container.Children.Add(labelAddress);

// Create the two textboxes, assign both to the second column and
// assign the second textbox to the second row.
TextBox textboxName = new() { Margin = new Thickness(2) };
Grid.SetColumn(textboxName, 1);
container.Children.Add(textboxName);

TextBox textboxAddress = new() { Margin = new Thickness(2) };
Grid.SetRow(textboxAddress, 1);
Grid.SetColumn(textboxAddress, 1);
container.Children.Add(textboxAddress);

// Create the two buttons, assign both to the third row and
// assign the second button to the second column.
Button buttonReset = new() { Margin = new Thickness(2), Content = "Reset" };
Grid.SetRow(buttonReset, 2);
container.Children.Add(buttonReset);

Button buttonSubmit = new() { Margin = new Thickness(2), Content = "Submit" };
Grid.SetColumn(buttonSubmit, 1);
Grid.SetRow(buttonSubmit, 2);
container.Children.Add(buttonSubmit);

// Create the popup window and assign the container (Grid) as its content
Window inputWindow = new()
{
    Title = "Input Record",
    Height = double.NaN,
    Width = 300,
    SizeToContent = SizeToContent.Height,
    Content = container
};

inputWindow.Show();
' Grid container which is the content of the Window
Dim container As New Grid() With {.Margin = New Thickness(5)}
container.RowDefinitions.Add(New RowDefinition())
container.RowDefinitions.Add(New RowDefinition())
container.RowDefinitions.Add(New RowDefinition())
container.ColumnDefinitions.Add(New ColumnDefinition())
container.ColumnDefinitions.Add(New ColumnDefinition())

' Create the two labels, assign the second label to the second row
Dim labelName As New Label() With {.Content = "Enter your name:"}
container.Children.Add(labelName)

Dim labelAddress As New Label() With {.Content = "Enter your address:"}
Grid.SetRow(labelAddress, 1)
container.Children.Add(labelAddress)

' Create the two textboxes, assign both to the second column and
' assign the second textbox to the second row.
Dim textboxName As New TextBox() With {.Margin = New Thickness(2)}
Grid.SetColumn(textboxName, 1)
container.Children.Add(textboxName)

Dim textboxAddress As New TextBox() With {.Margin = New Thickness(2)}
Grid.SetRow(textboxAddress, 1)
Grid.SetColumn(textboxAddress, 1)
container.Children.Add(textboxAddress)

' Create the two buttons, assign both to the third row and
' assign the second button to the second column.
Dim buttonReset As New Button() With {.Margin = New Thickness(2), .Content = "Reset"}
Grid.SetRow(buttonReset, 2)
container.Children.Add(buttonReset)

Dim buttonSubmit As New Button() With {.Margin = New Thickness(2), .Content = "Submit"}
Grid.SetColumn(buttonSubmit, 1)
Grid.SetRow(buttonSubmit, 2)
container.Children.Add(buttonSubmit)

' Create the window and assign the container (Grid) as its content
Dim inputWindow As New Window() With
{
    .Title = "Input Record",
    .Height = Double.NaN,
    .Width = 300,
    .SizeToContent = SizeToContent.Height,
    .Content = container
}

inputWindow.Show()

이벤트 구독

XAML 또는 코드를 사용하여 컨트롤의 이벤트를 구독할 수 있지만 코드로만 이벤트를 처리할 수 있습니다.

XAML에서 이벤트는 요소의 특성으로 설정됩니다. 이벤트에서는 표기법 <Element.Event>handler<Element.Event>을 사용할 수 없습니다. 다음 코드 스니펫은 ClickButton 이벤트를 구독하는 방법을 보여줍니다.

<Button Click="Submit_Click" Grid.Row="2" Grid.Column="1" Name="Submit" Margin="2">Submit</Button>

코드에서 동일한 작업을 수행하는 방법은 다음과 같습니다.

Button buttonSubmit = new() { Margin = new Thickness(2), Content = "Submit" };
buttonSubmit.Click += Submit_Click;
Dim buttonSubmit As New Button() With {.Margin = New Thickness(2), .Content = "Submit"}
AddHandler buttonSubmit.Click, AddressOf Submit_Click

다음 코드 조각은 Click 이벤트를 Button의 것으로 처리합니다.

private void Submit_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Someone clicked the submit button.");
}
Private Sub Submit_Click(sender As Object, e As Windows.RoutedEventArgs)
    MessageBox.Show("Someone clicked the submit button.")
End Sub

컨트롤의 모양 변경

앱의 모양과 느낌에 맞게 컨트롤의 모양을 변경하는 것이 일반적입니다. 수행하려는 작업에 따라 다음 중 하나를 수행하여 컨트롤의 모양을 변경할 수 있습니다.

  • 컨트롤의 속성 값을 변경합니다.
  • 컨트롤의 Style을 만듭니다.
  • 컨트롤의 새 ControlTemplate을 만듭니다.

컨트롤의 속성 변경

많은 컨트롤에는 단추의 배경과 같이 컨트롤이 표시되는 방식을 변경할 수 있는 속성이 있습니다. XAML 및 코드 둘 다에서 값 속성을 설정할 수 있습니다. 다음 예제는 XAML에서 BackgroundFontSize, FontWeight, 및 Button 속성을 설정합니다.

<Button Grid.Row="2" Grid.Column="1" Name="Submit" Margin="2" Content="Submit">
    <Button.FontSize>18</Button.FontSize>
    <Button.FontWeight>Bold</Button.FontWeight>
    <Button.Background>
        <LinearGradientBrush>
            <GradientStop Color="#0073E6" Offset="0.0" />
            <GradientStop Color="#81D4FA" Offset="0.9" />
        </LinearGradientBrush>
    </Button.Background>
</Button>

코드에서 동일한 작업을 수행하는 방법은 다음과 같습니다.

Button buttonSubmit = new() { Margin = new Thickness(2), Content = "Submit" };
buttonSubmit.FontSize = 18f;
buttonSubmit.FontWeight = FontWeights.Bold;
buttonSubmit.Background =
    new LinearGradientBrush(
        (Color)ColorConverter.ConvertFromString("#0073E6"),
        (Color)ColorConverter.ConvertFromString("#81D4FA"),
        new Point(0d, 0d),
        new Point(0.9d, 0d));
Dim buttonSubmit As New Button() With {.Margin = New Thickness(2), .Content = "Submit"}
buttonSubmit.FontSize = 18.0F
buttonSubmit.FontWeight = FontWeights.Bold
buttonSubmit.Background =
        New LinearGradientBrush(
            ColorConverter.ConvertFromString("#0073E6"),
            ColorConverter.ConvertFromString("#81D4FA"),
            New Point(0D, 0D),
            New Point(0.9D, 0D))

이제 예제 창은 다음 이미지와 같습니다.

이름과 주소에 레이블이 지정된 두 개의 텍스트 상자가 있는 WPF 앱의 스크린샷 두 개의 단추가 표시됩니다. 한 단추의 이름은 '초기화'이고 다른 단추는 '제출'입니다. '제출' 단추에는 파란색에서 밝은 파란색으로 전환되는 그라데이션 배경이 있습니다.

컨트롤에 대한 스타일 만들기

WPF는 각 컨트롤에서 속성을 설정하는 대신 컨트롤의 모양을 지정 Style하는 광범위한 기능을 제공합니다. Style 정의는 일반적으로 ResourceDictionary에서 정의됩니다. ResourceDictionary은 컨트롤 또는 창의 속성과 같은 XAML에 포함됩니다. 리소스는 선언된 범위에 적용됩니다. 자세한 내용은 XAML 리소스 개요를 참조하세요.

다음 예제에서는 스타일을 정의하는 동일한 Style에 포함된 모든 ButtonGrid를 적용합니다.

<Grid.Resources>
    <Style TargetType="{x:Type Button}">
        <Style.Setters>
            <Setter Property="FontSize" Value="18" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Background">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Color="#0073E6" Offset="0.0" />
                        <GradientStop Color="#81D4FA" Offset="0.9" />
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>
</Grid.Resources>

코드에서 동일한 작업을 수행하는 방법은 다음과 같습니다.

Grid container = new() { Margin = new Thickness(5) };
container.RowDefinitions.Add(new RowDefinition());
container.RowDefinitions.Add(new RowDefinition());
container.RowDefinitions.Add(new RowDefinition());
container.ColumnDefinitions.Add(new ColumnDefinition());
container.ColumnDefinitions.Add(new ColumnDefinition());

Style buttonStyle = new(typeof(Button));
buttonStyle.Setters.Add(new Setter(Button.FontSizeProperty, 18d));
buttonStyle.Setters.Add(new Setter(Button.FontWeightProperty, FontWeights.Bold));
buttonStyle.Setters.Add(new Setter(Button.BackgroundProperty,
    new LinearGradientBrush(
        (Color)ColorConverter.ConvertFromString("#0073E6"),
        (Color)ColorConverter.ConvertFromString("#81D4FA"),
        new Point(0d, 0d),
        new Point(0.9d, 0d))));

container.Resources.Add(typeof(Button), buttonStyle);
Dim container As New Grid() With {.Margin = New Thickness(5)}
container.RowDefinitions.Add(New RowDefinition())
container.RowDefinitions.Add(New RowDefinition())
container.RowDefinitions.Add(New RowDefinition())
container.ColumnDefinitions.Add(New ColumnDefinition())
container.ColumnDefinitions.Add(New ColumnDefinition())

Dim buttonStyle As New Style(GetType(Button))
buttonStyle.Setters.Add(New Setter(Button.FontSizeProperty, 18.0R))
buttonStyle.Setters.Add(New Setter(Button.FontWeightProperty, FontWeights.Bold))
buttonStyle.Setters.Add(New Setter(Button.BackgroundProperty,
        New LinearGradientBrush(
            ColorConverter.ConvertFromString("#0073E6"),
            ColorConverter.ConvertFromString("#81D4FA"),
            New Point(0D, 0D),
            New Point(0.9D, 0D))))

container.Resources.Add(GetType(Button), buttonStyle)

다음 이미지는 창의 표에 적용된 스타일을 보여 줍니다. 이 스타일은 두 단추의 모양을 변경합니다.

이름과 주소에 레이블이 지정된 두 개의 텍스트 상자가 있는 WPF 앱의 스크린샷 두 개의 단추가 표시됩니다. 한 단추의 이름은 '초기화'이고 다른 단추는 '제출'입니다. 두 단추 모두 파란색에서 밝은 파란색으로 전환되는 그라데이션 배경을 특징으로 합니다.

특정 형식의 모든 컨트롤에 스타일을 적용하는 대신 리소스 사전의 스타일에 키를 추가하고 컨트롤 속성에서 해당 키를 참조하여 특정 컨트롤에 Style 할당할 수도 있습니다. 스타일에 대한 자세한 내용은 스타일 지정 및 템플릿을 참조하세요.

ControlTemplate 만들기

A Style를 사용하면 여러 컨트롤에서 속성을 한 번에 설정할 수 있지만, 때로는 Style 이상으로 컨트롤의 모양을 사용자 지정하고 싶을 수 있습니다. Control 클래스를 상속받은 클래스는 컨트롤의 구조와 모양을 정의하는 ControlTemplate을 갖고 있습니다.

거의 모든 앱에서 사용하는 일반적인 Button 컨트롤을 고려합니다. 단추의 기본 동작은 사용자가 단추를 선택할 때 앱이 일부 작업을 수행할 수 있도록 하는 것입니다. 기본적으로 WPF의 버튼은 돋아오른 사각형으로 나타납니다. 앱을 개발할 때, 단추의 동작, 즉 사용자가 단추를 조작하여 Click 이벤트를 발생시키는 방식의 활용을 고려할 수 있습니다. 또한 단추의 속성 변경만으로는 할 수 없는 모양 변경을 포함하여 외관을 더욱 다양하게 변경할 수도 있습니다. 이러한 경우 새 ControlTemplate을 만들 수 있습니다.

다음 예제는 ControlTemplate 에 대 한는 Button합니다. ControlTemplateButton을(를) 위한 시각적 요소를 생성하여, 둥근 모서리와 그라데이션 배경이 있는 테두리를 나타냅니다.

<Button Grid.Row="2" Grid.Column="1" Name="Submit" Margin="2" Content="Submit">
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Border Name="Border" CornerRadius="10" BorderThickness="1" BorderBrush="Black">
                <Border.Background>
                    <LinearGradientBrush StartPoint="0,0.5" 
                         EndPoint="1,0.5">
                        <GradientStop Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
                        <GradientStop Color="PeachPuff" Offset="0.9" />
                    </LinearGradientBrush>
                </Border.Background>
                <ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
            </Border>
            <ControlTemplate.Triggers>
                <!--Change the appearance of the button when the user clicks it.-->
                <Trigger Property="IsPressed" Value="true">
                    <Setter TargetName="Border" Property="Background">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                                <GradientStop Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
                                <GradientStop Color="LightBlue" Offset="0.9" />
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Button.Template>
</Button>

비고

ButtonBackground 속성을 SolidColorBrush로 설정해야 예제가 정상적으로 작동합니다.

코드에서 동일한 작업을 수행하는 방법은 다음과 같습니다. 다음 코드는 XAML 문자열을 만들고 구문 분석하여 적용할 수 있는 템플릿을 생성합니다. 이는 런타임에 템플릿을 생성하는 지원되는 방법입니다.

Button buttonSubmit = new() { Margin = new Thickness(2), Content = "Submit" };

// Create the XAML used to define the button template
const string xaml = """
    <ControlTemplate TargetType="Button">
        <Border Name="Border" CornerRadius="10" BorderThickness="1" BorderBrush="Black">
            <Border.Background>
                <LinearGradientBrush StartPoint="0,0.5" 
                     EndPoint="1,0.5">
                    <GradientStop Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
                    <GradientStop Color="PeachPuff" Offset="0.9" />
                </LinearGradientBrush>
            </Border.Background>
            <ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
        </Border>
        <ControlTemplate.Triggers>
            <!--Change the appearance of the button when the user clicks it.-->
            <Trigger Property="IsPressed" Value="true">
                <Setter TargetName="Border" Property="Background">
                    <Setter.Value>
                        <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                            <GradientStop Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
                            <GradientStop Color="LightBlue" Offset="0.9" />
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
    """;

// Load the XAML into a stream that can be parsed
using MemoryStream stream = new(System.Text.Encoding.UTF8.GetBytes(xaml));

// Create a parser context and add the default namespace and 
// the x namespace, which is common to WPF XAML
System.Windows.Markup.ParserContext context = new();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");

// Parse the XAML and assign it to the button's template
buttonSubmit.Template = (ControlTemplate)System.Windows.Markup.XamlReader.Load(stream, context);

// Set the other properties of the button
Grid.SetColumn(buttonSubmit, 1);
Grid.SetRow(buttonSubmit, 2);

// Assign the button to the grid container
container.Children.Add(buttonSubmit);
Dim buttonSubmit As New Button() With {.Margin = New Thickness(2), .Content = "Submit"}

' Create the XAML used to define the button template
Const xaml As String = "
    <ControlTemplate TargetType=""Button"">
        <Border Name=""Border"" CornerRadius=""10"" BorderThickness=""1"" BorderBrush=""Black"">
            <Border.Background>
                <LinearGradientBrush StartPoint=""0,0.5"" 
                     EndPoint=""1,0.5"">
                    <GradientStop Color=""{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}"" Offset=""0.0"" />
                    <GradientStop Color=""PeachPuff"" Offset=""0.9"" />
                </LinearGradientBrush>
            </Border.Background>
            <ContentPresenter Margin=""2"" HorizontalAlignment=""Center"" VerticalAlignment=""Center"" RecognizesAccessKey=""True""/>
        </Border>
        <ControlTemplate.Triggers>
            <!--Change the appearance of the button when the user clicks it.-->
            <Trigger Property=""IsPressed"" Value=""true"">
                <Setter TargetName=""Border"" Property=""Background"">
                    <Setter.Value>
                        <LinearGradientBrush StartPoint=""0,0.5"" EndPoint=""1,0.5"">
                            <GradientStop Color=""{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}"" Offset=""0.0"" />
                            <GradientStop Color=""LightBlue"" Offset=""0.9"" />
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>"

' Load the XAML into a stream that can be parsed
Using stream As New MemoryStream(System.Text.Encoding.UTF8.GetBytes(xaml))

    ' Create a parser context and add the default namespace and 
    ' the x namespace, which is common to WPF XAML
    Dim context = New System.Windows.Markup.ParserContext()
    context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation")
    context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml")

    ' Parse the XAML and assign it to the button's template
    buttonSubmit.Template = System.Windows.Markup.XamlReader.Load(stream, context)
End Using

' Set the other properties of the button
Grid.SetColumn(buttonSubmit, 1)
Grid.SetRow(buttonSubmit, 2)

' Assign the button to the grid container
container.Children.Add(buttonSubmit)

다음 이미지는 적용할 때 템플릿의 모양을 보여줍니다.

이름과 주소에 레이블이 지정된 두 개의 텍스트 상자가 있는 WPF 앱의 스크린샷 두 개의 단추가 표시됩니다. 한 단추의 이름은 '초기화'이고 다른 단추는 '제출'입니다. '제출' 단추에는 둥근 모서리와 복숭아 색이 적용되어 있습니다.

이전 예제 ControlTemplate 에서는 단일 단추에 적용됩니다. 그러나 ControlTemplateStyle에 할당하고 모든 버튼에 적용할 수 있으며, 이는 컨트롤에 대한 스타일 만들기 섹션에서 설명한 것과 같습니다.

템플릿에서 제공하는 고유한 기능을 활용하는 방법에 대한 자세한 내용은 스타일 지정 및 템플릿을 참조하세요.

컨트롤에 풍부한 콘텐츠

Control 클래스에서 상속받는 대부분의 클래스는 서식 있는 콘텐츠를 포함할 수 있습니다. 예를 들어 Label은 문자열, Image 또는 Panel과 같은 개체를 포함할 수 있습니다. 다음 클래스는 서식 있는 콘텐츠에 대한 지원을 제공하고 WPF의 대부분의 컨트롤에 대한 기본 클래스 역할을 합니다.