본문 바로가기

.Net Technology/WPF

(6) 비주얼 스튜디오를 이용하여 WPF 애플리케이션 만들기

지금까지의 예제들은 텍스트 편집기와 명령 프롬포트, XAML패드와 같은 도구를 이용해서만 만들어 왔었다. 이렇게 별도의 툴없이 작업을 진행해 온 이유는 WPF 애플리케이션의 기본적인 문법에 초점을 맞추기 위해서였다. 하지만 이번에는 비주얼 스튜디오가 얼만큼 개발을 간단히 할 수 있게 지원해 주는지 살펴보도록 하겠다. 

WPF 프로젝트 템플릿

비주얼 스튜디오의 새로운 프로젝트 대화상자를 열어보면 Visual C# 이란 메뉴의 Window 라는 소메뉴를 클릭해보면 WPF 프로젝트가 정의되어 있는 것을 볼 수 있다. 다음 [그림16] 에서처럼 WPF 프로젝트를 선택할 수 있을 뿐만 아니라 WPF 사용자 정의 컨트롤, WPF 사용자 지정 컨트롤, WPF 브라우저 응용프로그램(XBAP)과 같은 프로젝트를 선택할 수 있다. 

 
[그림16] 비주얼 스튜디오 2008의 WPF 템플릿 


WPF 응용 프로그램을 만들고 싶다면 당연히 WPF 응용 프로그램 프로젝트를 선택할 것이다. 이 프로젝트를 생성하면 각각의 PresentationCore.dll, PresentationFoundation.dll, WindowsBase.dll과 같은 WPF 어셈블리들이 참조되고 또한 Window와 Application 클래스를 상속받은 XAML이 정의되어 있을 것이다. 

 
[그림17] WPF 프로젝트 타입의 초기화 파일


초기 창의 이름 바꾸기

제대로 된 윈도우 프로그램을 만들기 위해서 분명 초기에 설정된 Window1이라는 기본 이름을 다른 이름으로 변경할 것이다. 하지만 WPF 애플리케이션에서는 윈도우 애플리케이션처럼 단순하게 바꿀 수 없고 보다 복잡하다. 그럼 이름을 바꾸는 방법에 대해서 설명해 보도록 하겠다. 

먼저 Window1.xaml 파일에서 오른쪽 마우스를 누르고 [이름 바꾸기]를 선택한다. 그럼 Windows1.xaml.cs 또한 바뀌는 것을 볼 수 있을 것이다. 하지만 xaml.cs 파일을 열어보면 클래스의 이름은 여전히 Window1로 남아있는 것을 볼 수 있을 것이다. 만약 *.xaml.cs 파일에서 오른쪽 마우스를 클릭한 후에 [리펙터링]->[이름바꾸기] 옵션을 선택해서 이름을 변경할 수 있을 것이다. 그리고 프로그램을 실행해보면 예외가 발생하는 것을 볼 수 있다. 

그 첫 번째 이유는 <Window> 엘리먼트를 열어보면 Class 애트리뷰트가 여전히 Window1으로 되어 있기 때문이다. 이 클래스를 수정해주어야 한다.

<Window x:
Class="MyWPFApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
</Grid>
</Window>


그리고 추가적으로 <Application> 엘리먼트 또한 열어서 StartupUri의 이름을 새로 바꾼 이름으로 수정해 주어야 한다.

<Application x:Class="MyWPFApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
>
<Application.Resources>
</Application.Resources>
</Application>



이렇게 수정한 후에 다시 실행해보면 에러 없이 창이 뜨는 것을 볼 수 있을 것이다.

Window 클래스를 WPF 프로젝트에 추가할 경우 파일의 이름을 잘 설정해 준다면 별도의 추가적인 작업을 요구하지 않을 것이다.



WPF 디자이너

27장에서 살펴본 윈도우 응용 프로그램과 비슷하게 비주얼 스튜디오 2008은 여러 개의 WPF 컨트롤을 가지고 있는 도구상자를 제공해주고, 또한 UI를 배치할 수 있는 비주얼 디자이너를 제공해주고 있다. 그리고 선택한 컨트롤의 속성들을 설정할 수 있는 속성창 또한 제공해 주고 있다. *.xaml을 위한 디자이너는 2개의 패널로 분리 되어있다. 기본적으로 위의 패널에는 현재 창의 모습을 볼 수 있고 나머지 아래 부분에는 현재 보여지는 창의 XAML을 볼 수 있다. 다음 [그림18]을 살펴보자. 
 

[그림18] WPF 디자이너

아래에는 [창 바꾸기] 버튼을 이용하면 비주얼 디자이너의 위치를 바꿀 수 있다. 예를 들어 이 버튼을 이용해서 위 아래로 창을 바꿀 수 있고 또한 상하 좌우로 창을 이동시켜서 자신이 편한 상태로 창을 셋팅할 수 있다.



XAML 패널 안에서 XAML언어를 작성한다고 한다면 인텔리센스를 이용해서 쉽게 XAML을 작성할 수 있을 것이다. 예를 들어 만약 Button을 <Grid> 안에 작성하고 싶다면 속성이나 이벤트와 같은 속성들을 직접 확인할 수 있다는 것이다. 다음 [그림19]는 속성을 찾을 수 있게 지원해주는 인텔리센스에 대한 내용을 보여주고 있다. 

 

[그림19] XAML 인텔리센스

윈도우 폼과는 다르게 WPF 애플리케이션에서 이벤트를 작성할 때 속성 창을 이용해서 더블 클릭해서 작성하지 않는다. 만약 WPF 이벤트를 만들고 싶다면 C# 문법을 이용해서 수동으로 이벤트를 등록해주어야 한다. 아니면 만약에 XAML안에서 이벤트 이름을 주고 싶다면 다음 [그림20]과 같이 New Event Handler와 같은 팝업이 뜨는 것을 볼 수 있다. 
 

[그림20] 비주얼 디자이너를 이용한 이벤트 생성

만약 XAML에서 손으로 직접 이벤트를 생성하고 싶다면 메서드 이름을 지정해주면 된다. 만약 IDE에서 <New Event Handler>를 직접 클릭해서생성하게 된다면 “컨트롤이름_이벤트이름”과 같은 형식으로 이름이 생성된다. 어떻게 하든 이벤트를 생성하면 다음과 같이 코드를 작성해 줄 수 있다.


private void Button_Click(object sender, RoutedEventArgs e)
{
}



 
만약 IDE에서 컨트롤의 멤버 변수를 정의하면 그 속성의 이름을 반드시 지정해 주어야 한다는 것을 기억하자. 그리고 만약 이름을 지정하지 않은 컨트롤의 이벤트를 생성하게 되면 그 이벤트의 이름은 “컨트롤명_이벤트명”과 같이 생성된다는 것을 알아두자.(Button_Click, Button_Click_1, Button_Click_2)

지금까지 WPF를 개발하기 위한 비주얼 스튜디오 2008의 기본적인 도구에 대해서 살펴보았다. 이제 XAML을 파싱하는 간단한 예제 프로그램을 직접 만들어 보도로 하자. 


XAML 처리 프로그램 SimpleXamlPad.exe 만들기

WPF 에서는 프로그램에서 XAML을 파싱하고 저장하고 또 불러올 수 있는 기능을 제공하는 API를 지원하고 있다. 때문에 여러모로 많은 도움을 받을 수 있다. 예를 들어 5개의 다른 UI를 나타내는 XAML 파일을 가지고 있다고 가정하자. 각각의 파일 안의 컨트롤들이 동일하다면 동적으로 스킨을 적용하는 것이 가능할 것이다. 

System.Windows.Markup 네임스페이스는 XamlReader와 XamlWriter 클래스를 제공하고 있다. 여기서 *.xaml 파일로부터 Window 클래스를 어떻게 다루는지 살펴보기 위해서 우리는 SimpleXamlPad라는 WPF 애플리케이션 프로젝트를 만들어 볼 것이다. 기능은 앞에서 살펴본 xamlpad.exe 애플리케이션과 비슷하게 만들어 볼 것이다. 

물론 우리가 만들 애플리케이션이 xamlpad.exe와 같은 풍부한 기능을 제공하지는 못할 것이지만 XAML을 작성하고 그 결과를 볼 수 있는 기능을 제공할 것이다. 또 외부파일로 XAML을 저장하는 기능 또한 제공할 것이다. 그럼 비주얼 스튜디오를 열어 SimpleXamlPad라는 프로젝트를 생성하고 초기 창의 이름을 MainWindow로 수정해 보도록 하자. 


다음 강좌에서는 컨트롤과 패널과 같은 자세한 내용들을 살펴볼 것이기 때문에 여기서 컨트롤을 다루는 내용에 대해서는 크게 생각하지 말고 넘어가도록 하자.

<Window x:Class="SimpleXamlPad.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Simple XAMl Viewer" Height="338" Width="1041"
Loaded="Window_Loaded" Closed="Window_Closed"
WindowStartupLocation="CenterScreen">
  <DockPanel LastChildFill="True" >
    <!-- 이 버튼은 정의된 XAML을 보여준다. -->
    <Button DockPanel.Dock="Top" Name = "btnViewXaml" Width="100" Height="40"
    Content ="View Xaml" Click="btnViewXaml_Click" />
    <!-- 여기에 XAML을 작성한다. -->
    <TextBox AcceptsReturn ="True" Name ="txtXamlData"
    FontSize ="14" Background="Black" Foreground="Yellow"
    BorderBrush ="Blue" VerticalScrollBarVisibility="Auto"
    AcceptsTab="True">
    </TextBox>
  </DockPanel>
</Window>



먼저 <Grid>를 <DockPanel> 클래스로 변경해 주었고 btnViewXaml이라는 Button과 txtXamlData라는 TextBox 컨트롤을 추가하였다. 그리고 Button에 Click 이벤트를 추가해주었다. 또한 Loaded와 Closed 이벤트를 <Window> 개체에 추가하였다. 만약 이렇게 XAML에서 이벤트를 선언했을 경우 MainWindow.xaml.cs에서 해당 이벤트를 구현해 주어야 한다. 

public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();
  }
  private void btnViewXaml_Click(object sender, RoutedEventArgs e)
  {
  }
  private void Window_Closed(object sender, EventArgs e)
  {
  }
  private void Window_Loaded(object sender, RoutedEventArgs e)
  {
  }
}


 
그리고 MainWindow.xaml.cs 클래스에 다음과 같은 네임스페이스를 참조하도록 하자.

using System.IO;
using System.Windows.Markup;



로드 이벤트의 구현

매인 윈도우의 Loaded 이벤트는 실행된 애플리케이션의 폴더에 YourXaml.xaml라는 파일이 있는지 확인하는 역할을 하게 된다. 만약 파일이 존재하면 데이터를 읽은 후에 TextBox에 보여주게 할 것이다. 만약 파일이 없다면 빈 TextBox에 기본적인 XAML을 할당해 줄 것이다. (여기서 <Grid>대신 <StackPanel>을 Window의 Content 속성에 묵시적으로 할당해 줄 것이다.) XAML 네임스페이스는 “”를 이용해서 표현하기 때문에 문자열이 조금 지저분해 지게된다.


private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // 애플리케이션을 로드할 때 텍스트박스에 특정 값을 넣어준다. 
    if (File.Exists(System.Environment.CurrentDirectory + "\\YourXaml.xaml"))
    {
        txtXamlData.Text = File.ReadAllText("YourXaml.xaml");
    }
    else
    {
        txtXamlData.Text =
        "<Window xmlns=\"http://schemas.microsoft.com"
        +"/winfx/2006/xaml/presentation\"\n"
        +"xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\""
        +" Height =\"400\" Width =\"500\" WindowStartupLocation=\"CenterScreen\">\n"
        +"<StackPanel>\n"
        +"</StackPanel>\n"
        +"</Window>";
    }
}


 
이렇게 하여 실행해보면 the SimpleXamlPad.exe 애플리케이션은 XAML이 텍스트박스에 나타나는 것을 볼 수 있을 것을 것이다. 다음 [그림21]은 지금까지 작성한 코드를 실행했을 때의 화면을 보여주고 있다. 
 

[그림21] SimpleXamlPad.exe의 실행 모습


버튼 클릭 이벤트의 구현

버튼을 클릭할 경우 현재 TextBox에 있는 데이터를 YourXaml.xaml이란 파일에 저장할 것이고 스트림 클래스를 기반으로 하는 File.Open()을 이용해서 데이터를 읽어올 것이다. 그리고 XamlReader.Load() 메서드는 System.String 타입이 아닌 스트림 기반의 타입을 받아 XAML을 파싱하여 보여주게 된다. 

그럼 코드를 작성해 보도록 하자. XAML을 메모리에 읽어온 후에 System.Windows.Window 인스턴스를 생성할 것이고 이 창을 새창으로 열어 보여줄 것이다.

private void btnViewXaml_Click(object sender, RoutedEventArgs e)
{
    // *.xaml 파일에 데이터를 작성한다. 
    File.WriteAllText("YourXaml.xaml", txtXamlData.Text);
    //동적으로 생성할 창
    Window myWindow = null;
    // *.xaml file을 연다.
    try
    {
        using (Stream sr = File.Open("YourXaml.xaml", FileMode.Open))
        {
            // Window 객체와 XAML을 할당한다.
            myWindow = (Window)XamlReader.Load(sr);
            myWindow.ShowDialog();
        }
    }
    catch (Exception ex)
    {     MessageBox.Show(ex.Message); }
}



try/catch 구문을 이용해서 XAML을 읽어오는 부분을 감싸 주었다. 만약 YourXaml.xaml 파일이 잘못된 마크업을 넣게 된다면 그 에러 메시지를 확인할 수 있을 것이다. 


Closed 이벤트의 구현

마지막으로 Window 클래스의 Closed 이벤트를 통해서 현재 마지막의 TextBox의 데이터를 YourXaml.xaml이란 파일로 저장시킬 것이고 그 데이터를 계속 유지할 수 있게 할 것이다. 

private void Window_Closed(object sender, EventArgs e)
{
    // *.xaml 파일을  Write out the data in the text block to a local *.xaml file.
    File.WriteAllText("YourXaml.xaml", txtXamlData.Text);
}



애플리케이션 테스트

프로그램을 실행하고 XAML을 텍스트박스에 넣어보자. 그럼 xamlpad.exe 처럼 애트리뷰트로 특정 이벤트나 다음과 같은 XAML 확장식을 이용할 수 없을 것이다. 테스트를 위해서 다음과 같은 코드를 넣어 실행해 보도록 하자.

<StackPanel>
<Rectangle Fill = "Green" Height = "40" Width = "200" />
<Button Content = "OK!" Height = "40" Width = "100" />
<Label Content ="{x:Type Label}" />
</StackPanel>



이 코드를 넣은 후에 버튼을 누르면 정의한 XAML이 다음 [그림22]와 같이 보여지는 것을 볼 수 있을 것이고, 잘못된 XAML을 넣게 되면 에러 메시지가 메시지 창으로 열리는 것을 볼 수 있을 것이다. 
 

[그림22] SimpleXamlPad.exe의 동작

이 애플리케이션을 통해서 WPF의 가능성에 대해서 많은 것을 생각해볼 수 있는 기회가 되었을 거라고 생각한다. 하지만 아직 WPF의 컨트롤이나 패널에 대해서 더 많이 알아보아야 할 것이다. 이 컨트롤에 대해서 살펴보기 전에 익스프레션 블렌드에 대해서 간단하게 살펴보도록 하자.


마이크로소프 익스프레션 블렌드의 역할

XAML과 WPF와 같은 새로운 기술이 개발자들에게는 가장 흥미로운 내용이긴 하지만 3D 이미지, 애니메이션과 같은 개체를 표현하기 위해서는 몇 천줄의 마크업 코드를 작성해야 할 것이다. 이런것을 비주얼 스튜디오 2008을 이용한다면 에러도 빈번하게 발생할 것이고 코드를 작성하는 것 자체가 굉장히 지루할 것이다. 즉, 비주얼 스튜디오 2008은 복잡한 코드를 작성하거나 XAML을 자동으로 생성해서 보여주기에는 상당히 부족한 것이 사실이다. 

WPF의 가장 큰 장점 중에 하나가 디자인과 코드의 분리라고 설명했었다. 하지만 WPF는 파일 계층에서 나누어 사용한다라는 것이 그렇게 간단하지 않다. 여기서 중요한 부분은 전문적인 WPF 애플리케이션을 만들기 위해서는 일반적으로 그래픽 디자이너를 섭외해서 사용할 것이다. 하지만 그 디자이너는 UI를 비주얼 스튜디오를 이용해서 표현하기란 정말 쉽지 않은 일이 된다는 것이다. 
이러한 문제들 때문에 마이크로소프트는 Expression 이라는 새로운 제품들을 만들기 시작했다. Expression 각각의 제품 소개는http://www.microsoft.com/expression 주소를 방문하면 볼 수 있을 것이다. 그 중에 Expression Blend에 대해서 간단하게 요약하자면 WPF의 UI를 개발하는 툴이라 할 수 있다.

익스프레션 블렌드의 장점

먼저 익스프레션 블렌드의 큰 장점은 디자이너가 기존부터 사용해오던 Adobe제품과 UI가 상당히 비슷하다는 것이다. 예를 들어 익스프레션 블렌드 툴에서 애니메이션을 만들기 위한 툴의 지원이나 색을 섞은 도구, 레이아웃이나 변형 도구들을 지원해주고 있는 것들이 상장히 비슷하다고 할 수 있다. 그리고 익스프레션 블렌드는 이벤트 트리거와 데이터 바인딩의 구현과 같은 기능을 추가적으로 지원해주고 있다. 때문에 디자이너는 XAML이나 C# 코드 없이도 쉽게 UI를 그려줄 수 있는 것이다.

 

[그림23] 익스프레션 블렌드로 디자인한 화면

다음으로 익스프레션 블렌드의 주요한 장점은 비주얼 스튜디오 2008과 같은 프로젝트 파일을 이용하여 만들어 진다는 것이다. 그러므로 디자이너는 UI를 익스프레션 블렌드로 디자인하고 개발자는 비주얼 스튜디오로 코드나 이벤트와 같은 작업을 진행할 수 있는 것이다. 
익스프레션 블렌드와 같은 툴을 이용하면 어느 정도 원하는 UI을 만들 수 있는 것은 사실이지만 이번 책은 WPF를 개발하기 위한 목적의 책이기 때문에 이 책에서는 자세한 내용을 다루지 않을 것이다. 하지만 만약 이 툴을 공부하고 싶다면 Microsoft Expression 사이트에 방문하면 보다 자세한 내용을 살펴볼 수 있을 것이다. 또한 이 툴은 트라이얼 버전으로 다운로드가 가능하다.



이 내용은 C#.NET Platform 원서의 내용을 기반으로 작성되었습니다.