자습서: .NET을 사용하여 새 WPF 앱 만들기
이 간단한 자습서에서는 Visual Studio를 사용하여 새 WPF(Windows Presentation Foundation) 앱을 만드는 방법을 알아봅니다. 초기 앱을 생성한 후에는 컨트롤을 추가하는 방법과 이벤트를 처리하는 방법을 알아봅니다. 이 자습서를 마치면 목록 상자에 이름을 추가하는 간단한 앱이 완성됩니다.
이 자습서에서는 다음 작업 방법을 알아봅니다.
- 새 WPF 앱 만들기
- 양식에 컨트롤 추가
- 컨트롤 이벤트를 처리하여 앱 기능 제공
- 앱 실행
이 자습서를 수행하는 동안 빌드할 앱의 미리 보기는 다음과 같습니다.
필수 조건
- Visual Studio 2022 버전 17.0 이상
- .NET 데스크톱 개발 워크로드를 선택합니다.
- .NET 6 개별 구성 요소를 선택합니다.
- Visual Studio 2022 버전 17.4 이상
- .NET 데스크톱 개발 워크로드를 선택합니다.
- .NET 7 개별 구성 요소를 선택합니다
팁
Visual Studio 2022 버전 17.4 이상을 사용하고 .NET 7 및 .NET 6 개별 구성 요소를 모두 설치합니다. Visual Studio 2022 버전 17.4에서 .NET 7 지원이 추가되었습니다.
WPF 앱 만들기
새 앱을 만드는 첫 번째 단계는 Visual Studio를 열고 템플릿을 통해 앱을 생성하는 것입니다.
Visual Studio를 엽니다.
새 프로젝트 만들기를 선택합니다.
템플릿 검색 상자에 wpf를 입력하고 Enter 키를 누릅니다.
코드 언어 드롭다운에서 C# 또는 Visual Basic을 선택합니다.
템플릿 목록에서 WPF 애플리케이션을 선택하고 다음을 선택합니다.
중요
WPF 애플리케이션(.NET Framework) 템플릿을 선택하지 마세요.
다음 이미지는 C# 및 Visual Basic .NET 프로젝트 템플릿을 모두 보여 줍니다. 코드 언어 필터를 적용한 경우 해당 템플릿이 표시됩니다.
새 프로젝트 구성 창에서 다음을 수행합니다.
- 프로젝트 이름 상자에 Names를 입력합니다.
- 동일한 디렉터리에 솔루션과 프로젝트 배치 확인란을 선택합니다.
- 필요에 따라 다른 위치를 선택하여 코드를 저장합니다.
- 다음 단추를 선택합니다.
추가 정보 창에서 대상 프레임워크에 대해 .NET 6.0(장기 지원)을 선택합니다. 생성 단추를 선택합니다.
Visual Studio를 엽니다.
새 프로젝트 만들기를 선택합니다.
템플릿 검색 상자에 wpf를 입력하고 Enter 키를 누릅니다.
코드 언어 드롭다운에서 C# 또는 Visual Basic을 선택합니다.
템플릿 목록에서 WPF 애플리케이션을 선택하고 다음을 선택합니다.
중요
WPF 애플리케이션(.NET Framework) 템플릿을 선택하지 마세요.
다음 이미지는 C# 및 Visual Basic .NET 프로젝트 템플릿을 모두 보여 줍니다. 코드 언어 필터를 적용한 경우 해당 템플릿이 표시됩니다.
새 프로젝트 구성 창에서 다음을 수행합니다.
- 프로젝트 이름 상자에 Names를 입력합니다.
- 동일한 디렉터리에 솔루션과 프로젝트 배치 확인란을 선택합니다.
- 필요에 따라 다른 위치를 선택하여 코드를 저장합니다.
- 다음 단추를 선택합니다.
추가 정보 창에서 대상 프레임워크에 대해 .NET 7.0(표준 기간 지원)을 선택합니다. 생성 단추를 선택합니다.
앱이 생성되면 Visual Studio에서 기본 창인 MainWindow의 XAML 디자이너 창이 열립니다. 디자이너가 표시되지 않는 경우 솔루션 탐색기 창에서 MainWindow.xaml 파일을 두 번 클릭하여 디자이너를 엽니다.
Visual Studio의 중요 부분
Visual Studio의 WPF 지원에는 앱을 만들 때 상호 작용하는 다섯 가지 중요한 구성 요소가 포함됩니다.
솔루션 탐색기
이 창에는 프로젝트 파일, 코드, 창, 리소스가 모두 표시됩니다.
속성
이 창에는 선택한 항목에 따라 구성할 수 있는 속성 설정이 표시됩니다. 예를 들어 솔루션 탐색기에서 항목을 선택하면 해당 파일과 관련된 속성 설정이 표시됩니다. 디자이너에서 개체를 선택하면 해당 항목과 관련된 설정이 표시됩니다.
도구 상자
도구 상자는 양식에 추가할 수 있는 모든 컨트롤을 포함합니다. 현재 양식에 컨트롤을 추가하려면 컨트롤을 두 번 클릭하거나 컨트롤을 끌어다 놓습니다.
XAML 디자이너
XAML 문서용 디자이너입니다. 대화형으로 작동하며, 도구 상자에서 개체를 끌어다 놓을 수 있습니다. 디자이너에서 항목을 선택하여 이동하면 앱 UI(사용자 인터페이스)를 시각적으로 구성할 수 있습니다.
디자이너와 편집기가 모두 표시되는 경우 하나에 대한 변경 내용이 다른 것에도 반영됩니다. 디자이너에서 항목을 선택하면 속성 창에 해당 개체에 대한 속성 및 특성이 표시됩니다.
XAML 코드 편집기
XAML 문서용 XAML 코드 편집기입니다. XAML 코드 편집기는 디자이너 없이 UI를 수동으로 작성하는 방법입니다. 컨트롤이 추가될 때 디자이너는 컨트롤의 속성 값을 유추할 수 있습니다. XAML 코드 편집기는 더 많은 제어 기능을 제공합니다.
디자이너와 편집기가 모두 표시되는 경우 하나에 대한 변경 내용이 다른 것에도 반영됩니다. 코드 편집기에서 텍스트 캐럿을 탐색할 때 속성 창에는 해당 개체에 대한 속성 및 특성이 표시됩니다.
XAML 검사
프로젝트를 만든 후에는 창을 표시하기 위한 최소한의 XAML 코드로 XAML 코드 편집기가 표시됩니다. 편집기가 열려 있지 않으면 솔루션 탐색기에서 MainWindow.xaml 항목을 두 번 클릭합니다. 다음 예제와 유사한 XAML이 표시됩니다.
<Window x:Class="Names.MainWindow"
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:Names"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
더 잘 이해할 수 있도록 이 XAML 코드를 분석해 보겠습니다. XAML은 WPF에서 사용하는 컴파일러에서 처리할 수 있는 단순한 XML입니다. WPF UI를 설명하고 .NET 코드와 상호 작용합니다. XAML을 이해하려면 최소한 XML의 기본 사항을 숙지해야 합니다.
문서 루트 <Window>
는 XAML 파일에서 설명하는 개체의 형식을 나타냅니다. 선언된 특성이 8개이며 이들은 일반적으로 세 개의 범주에 속합니다.
네임스페이스
XML 네임스페이스는 XML에 대한 구조체를 제공하여 파일에서 선언할 수 있는 XML 콘텐츠를 결정합니다.
기본
xmlns
특성은 전체 파일에 대한 XML 네임스페이스를 가져오며 이 경우에는 WPF에 의해 선언된 형식에 매핑됩니다. 다른 XML 네임스페이스는 접두사를 선언하고 XAML 파일에 대한 다른 형식 및 개체를 가져옵니다. 예를 들어xmlns:local
네임스페이스는local
접두사를 선언하고 프로젝트에 의해 선언된 개체,Names
코드 네임스페이스에서 선언된 개체에 매핑됩니다.x:Class
특성이 특성은
<Window>
를 코드에 의해 정의된 형식, 즉 MainWindow.xaml.cs 또는 MainWindow.xaml.vb 파일(Names.MainWindow
클래스)에 매핑합니다.Title
특성XAML 개체에 선언된 모든 일반 특성은 해당 개체의 속성을 설정합니다. 이 경우
Title
특성은Window.Title
속성을 설정합니다.
창 변경
먼저 프로젝트를 실행하고 기본 출력을 확인합니다. 컨트롤 없이 팝업되는 창과 MainWindow의 제목을 볼 수 있습니다.
예제 앱의 경우 이 창이 너무 크며 제목 표시줄이 설명을 제공하지 않습니다. XAML의 적절한 특성을 다음 값으로 변경하여 창의 제목과 크기를 변경합니다.
<Window x:Class="Names.MainWindow"
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:Names"
mc:Ignorable="d"
Title="Names" Height="180" Width="260">
<Grid>
</Grid>
</Window>
레이아웃 준비
WPF는 다양한 레이아웃 컨트롤을 갖춘 강력한 레이아웃 시스템을 제공합니다. 레이아웃 컨트롤은 자식 컨트롤을 배치하고 크기를 조정하는 데 도움이 되며 이를 자동으로 수행할 수도 있습니다. 이 XAML에서 사용자에게 제공되는 기본 레이아웃 컨트롤은 <Grid>
컨트롤입니다.
Grid
컨트롤을 사용하면 테이블과 매우 유사한 행과 열을 정의하고 특정 행 및 열 조합의 범위 내에 컨트롤을 배치할 수 있습니다. 원하는 수의 자식 컨트롤이나 다른 레이아웃 컨트롤을 Grid
에 추가할 수 있습니다. 예를 들어 특정 행과 열 조합에 다른 Grid
컨트롤을 배치할 수 있으며 새 Grid
는 더 많은 행과 열을 정의하고 자체 자식 항목을 포함할 수 있습니다.
<Grid>
컨트롤은 컨트롤이 배치될 행과 열을 정의합니다. 그리드에는 항상 단일 행과 열이 선언되어 있습니다. 즉, 기본적으로 그리드는 단일 셀입니다. 이 상태에서는 컨트롤을 유연하게 배치할 수 없습니다.
새 행과 열을 추가하기 전에 <Grid>
요소에 새 특성 Margin="10"
을 추가합니다. 이렇게 하면 창에서 그리드를 삽입하고 더 나은 외양을 만들 수 있습니다.
그런 다음 두 개의 행과 두 개의 열을 정의하여 표를 4개의 셀로 나눕니다.
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
XAML 코드 편집기 또는 XAML 디자이너에서 그리드를 선택하면 XAML 디자이너에 각 행과 열이 표시되는 것을 확인할 수 있습니다.
첫 번째 컨트롤 추가
이제 그리드를 만들었으므로 컨트롤 추가를 시작할 수 있습니다. 먼저 레이블 컨트롤로 시작합니다. 행 및 열 정의 후에 <Grid>
요소 내에 새 <Label>
요소를 만들고 Names
의 문자열 값을 제공합니다.
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label>Names</Label>
</Grid>
<Label>Names</Label>
는 Names
콘텐츠를 정의합니다. 일부 컨트롤은 콘텐츠를 처리하는 방법을 이해하고 나머지는 이해하지 못합니다. 컨트롤의 콘텐츠는 Content
속성에 매핑됩니다. XAML 특성 구문을 통해 콘텐츠를 설정할 때 <Label Content="Names" />
형식을 사용하게 됩니다. 두 가지 방법 모두 Names
텍스트를 표시하도록 레이블의 콘텐츠를 설정하여 동일한 작업을 수행합니다.
그러나 문제가 있습니다. 레이블이 그리드의 첫 번째 행과 열에 자동으로 할당되어서 창의 절반을 차지합니다. 첫 번째 행의 경우 레이블에 해당 행만 사용할 것이므로 많은 공간이 필요하지 않습니다. 첫 번째 <RowDefinition>
의 Height
특성을 *
에서 Auto
로 변경합니다. Auto
값은 그리드 행의 크기를 콘텐츠(이 경우 레이블 컨트롤)의 크기로 자동으로 조정합니다.
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
이제 디자이너에는 사용 가능한 높이의 작은 공간을 차지하는 레이블이 표시됩니다. 이제 다음 행이 차지할 공간이 더 마련되었습니다. 대부분의 컨트롤은 자신이 차지해야 할 가장 적합한 일종의 높이와 너비 값을 정의합니다. 예를 들어 레이블 컨트롤의 경우에는 사용자가 읽을 수 있는 정도의 높이 값이 있습니다.
컨트롤 배치
컨트롤 배치에 대해 알아보겠습니다. 위의 섹션에서 만든 레이블은 그리드의 0행과 0열에 자동으로 배치되었습니다. 행과 열의 번호는 0부터 시작하며 새 행 또는 열마다 1씩 증가합니다. 컨트롤은 그리드에 대해 아무것도 모르며 그리드 내의 배치를 제어하는 어떤 속성도 정의하지 않습니다. 컨트롤 배치 방법을 정의하는 자체 규칙 세트가 있는 다른 레이아웃 컨트롤 내에 컨트롤을 배치할 수도 있습니다.
컨트롤이 그리드를 알지 못하는데 어떻게 다른 행이나 열을 사용하도록 컨트롤에 지시합니까? 연결된 속성을 통해 가능합니다. 그리드는 WPF에서 제공하는 강력한 속성 시스템을 활용합니다. 그리드는 자식 컨트롤이 선언하고 사용할 수 있는 새 속성을 정의합니다. 속성은 실제로 컨트롤 자체에 존재하지 않으며 컨트롤이 그리드에 추가될 때 그리드에 의해 연결됩니다.
그리드는 자식 컨트롤의 행과 열 배치를 결정하는 두 가지 속성인 Grid.Row
및 Grid.Column
을 정의합니다. 이러한 속성이 컨트롤에서 생략되면 기본값이 0임을 의미하므로 컨트롤은 그리드의 0
행과 0
열에 배치됩니다. Grid.Column
특성을 1
로 설정하여 <Label>
컨트롤의 배치를 변경해 봅니다.
<Label Grid.Column="1">Names</Label>
이제 레이블이 어떻게 두 번째 열로 이동했는지 확인합니다. 다음에 만들 컨트롤을 배치하기 위해 연결된 속성 Grid.Row
및 Grid.Column
을 사용할 수 있습니다. 하지만 지금은 레이블을 열 0으로 복원합니다.
이름 목록 상자 만들기
이제 그리드 크기가 올바르게 조정되고 레이블이 만들어졌으므로 레이블 아래 행에 목록 상자 컨트롤을 추가합니다. 목록 상자를 행 1
및 열 0
에 배치할 것입니다. 또한 이 컨트롤에 lstNames
의 이름을 지정할 것입니다. 컨트롤의 이름이 지정되면 코드 숨김에서 참조할 수 있습니다. 이름은 x:Name
특성과 함께 컨트롤에 할당됩니다.
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label>Names</Label>
<ListBox Grid.Row="1" x:Name="lstNames" />
</Grid>
나머지 컨트롤 추가
마지막으로 추가할 두 개의 컨트롤은 텍스트 상자와 단추입니다. 사용자는 목록 상자에 추가할 이름을 입력하는 데 이를 사용합니다. 그러나 그리드에 대해 더 많은 행과 열을 만드는 대신 이 컨트롤을 <StackPanel>
레이아웃 컨트롤에 배치할 것입니다.
스택 패널은 컨트롤이 배치되는 방법에서 그리드와 다릅니다. 연결된 속성 Grid.Row
및 Grid.Column
을 사용하여 컨트롤을 배치할 위치를 그리드에 지시하는 동안, 스택 패널은 첫 번째 컨트롤을 배치하고 그 뒤에 다음 컨트롤을 배치하여 모든 컨트롤이 배치될 때까지 계속해서 자동으로 작동합니다. 각 컨트롤을 다른 컨트롤 아래에 “쌓아” 둡니다.
목록 상자 이후 <StackPanel>
컨트롤을 만들고 그리드 행 1
및 열 1
에 배치합니다. 값이 5,0,0,0
인 Margin
이라는 다른 특성을 추가합니다.
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label>Names</Label>
<ListBox Grid.Row="1" x:Name="lstNames" />
<StackPanel Grid.Row="1" Grid.Column="1" Margin="5,0,0,0">
</StackPanel>
Margin
특성은 이전에 그리드에서 사용되었지만 단일 값 10
만 입력했습니다. 이제 스택 패널에서 5,0,0,0
값을 사용했습니다. 여백은 Thickness
형식이며 두 값을 모두 해석할 수 있습니다. 두께는 직사각형 프레임에서 각 측면 주변의 공간, 즉 왼쪽, 위쪽, 오른쪽, 아래쪽을 각각 정의합니다. 여백 값이 단일 값이면 네 개의 면에 모두 해당 값이 사용됩니다.
그런 다음 <StackPanel>
에서 <TextBox>
및 <Button>
컨트롤을 만듭니다.
<StackPanel Grid.Row="1" Grid.Column="1" Margin="5,0,0,0">
<TextBox x:Name="txtName" />
<Button x:Name="btnAdd" Margin="0,5,0,0">Add Name</Button>
</StackPanel>
창에 대한 레이아웃이 완료되었습니다. 그러나 앱에는 실제로 작동하기 위한 논리가 없습니다. 이제 컨트롤 이벤트를 코드에 연결하고 앱이 실제로 작업을 수행하도록 해야 합니다.
Click 이벤트에 대한 코드 추가
우리가 만든 <Button>
에는 사용자가 단추를 누를 때 발생하는 Click
이벤트가 있습니다. 이 이벤트를 구독하고 목록 상자에 이름을 추가하는 코드를 추가할 수 있습니다. XAML 특성을 추가하여 컨트롤에서 속성을 설정하는 것처럼 XAML 특성을 사용하여 이벤트를 구독할 수 있습니다. Click
특성을 ButtonAddName_Click
으로 설정합니다.
<StackPanel Grid.Row="1" Grid.Column="1" Margin="5,0,0,0">
<TextBox x:Name="txtName" />
<Button x:Name="btnAdd" Margin="0,5,0,0" Click="ButtonAddName_Click">Add Name</Button>
</StackPanel>
이제 처리기 코드를 생성해야 합니다. ButtonAddName_Click
을 마우스 오른쪽 단추로 클릭하고 정의로 이동을 선택합니다. 이 작업은 입력한 처리기 이름과 일치하는 메서드를 코드 숨김에서 생성합니다.
private void ButtonAddName_Click(object sender, RoutedEventArgs e)
{
}
Private Sub ButtonAddName_Click(sender As Object, e As RoutedEventArgs)
End Sub
이제 세 단계를 수행하여 다음 코드를 추가합니다.
- 텍스트 상자에 이름이 포함되어 있는지 확인합니다.
- 텍스트 상자에 입력한 이름이 아직 존재하지 않는지 확인합니다.
- 목록 상자에 이름을 추가합니다.
private void ButtonAddName_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(txtName.Text) && !lstNames.Items.Contains(txtName.Text))
{
lstNames.Items.Add(txtName.Text);
txtName.Clear();
}
}
Private Sub ButtonAddName_Click(sender As Object, e As RoutedEventArgs)
If Not String.IsNullOrWhiteSpace(txtName.Text) And Not lstNames.Items.Contains(txtName.Text) Then
lstNames.Items.Add(txtName.Text)
txtName.Clear()
End If
End Sub
앱 실행
이제 이벤트가 코딩되었으므로 F5 키를 누르거나 메뉴에서 디버그>디버깅 시작을 선택하여 앱을 실행할 수 있습니다. 창이 표시되면 텍스트 상자에 이름을 입력한 다음 단추를 클릭하여 이름을 추가할 수 있습니다.
다음 단계
.NET Desktop feedback