본문 바로가기

.Net Technology/WPF

(7) WPF 컨트롤 라이브러리의 분해

WPF 프로그래밍 모델에 대해서 살펴보았고 Window와 Application 객체에 대해서 살펴보았다. 또한 XAML(Extendable Application Markup Language)에 대한 문법들과 여러 가지 이슈들에 대해서 살펴보았다. 이번 장에서는 WPF 컨트롤들에 대해서 살펴볼 것이고 WPF 컨트롤의 구조에 대해서도 살펴 볼 것이다. 그리고 의존 속성과 이벤트 라우팅에 대한 내용 또한 살펴볼 것이다. 

앞에서 이미 기본적인 프로그래밍 모델에 대해서 살펴보았고 이번 장에서는 WPF 컨트롤들을 이용하는 방법에 대해서 살펴볼 것이다. 예를 들어 다양한 WPF 컨데이너들이(Canvas, Grid, StackPanel, WrapPanel등) 어떻게 동작되는지 살펴볼 것이고 또한 툴바, 메뉴, 상태바와 같은 컨트롤들을 이용해서 매인 윈도우를 어떻게 완성하는지 살펴볼 것이다. 그리고 데이터 바인딩 모델에 대해서 살펴볼 것이다.

대부분의 컨트롤들의 XAML은 “느슨한 XAML파일” 로서 코드 다운로드 안에 포함되어 왔다. SimpleXamlPad.exe을 만들 때는 외부로 렌더링 된 모습을 보기 위해서 마크업을 특정 파일로 저장한 후에 보여주었다. 이와 다르게 <Window>와 </Window>를 <Page>와</Page>로 변경할 수 있고 더블클릭 하여 인터넷 익스플로러에서 보는 것이 가능하다.



WPF 컨트롤 라이브러리의 분해


만약 아주 새로운 컨셉의 UI 그래픽을 만드는 것이 아니라면 기본적인 WPF 컨트롤들을 이용한 프로그램은 크게 사람들을 놀라게 하지 않을 것이다. 과거에는 MFC, Java AWT/Swing, Windows Forms, VB 6.0, Mac OS X [Cocoa], GTK+/GTK# 등과 같은 툴킷을 이용해서 UI를 개발해 왔을 것이다. 다음 [표29-1]은 관련된 기술별로 정리한 WPF 컨트롤들에 대해서 보여주고 있다. 

카테고리

컨트롤


입력 타입의 컨트롤

Button, RadioButton, ComboBox, CheckBox, Expander, ListBox, lider, ToggleButton, TreeView, ContextMenu, ScrollBar, Slider, TabControl, TextBox, RepeatButton, RichTextBox, Label

WPF는 지금까지 사용해오던 모든 기본 컨트롤들을 제공하고 있다.

윈도우 프레임 기반의 컨트롤

Menu, ToolBar, StatusBar, ToolTip, ProgressBar

이러한 UI Window 객체에 프레임을 나누기 위해서 이용한다그리고 특별한 상태정보를 보여주기 위해서 이용하기도 한다.

미디어 컨트롤

Image, MediaElement, SoundPlayerAction

이러한 미디어 컨트롤들은 오디오/비디오를 재생할 수 있게 해주고 또한 이미지를 보여주기 위해서 사용한다.

레이아웃 컨트롤

Canvas, DockPanel, Grid, GridView, GroupBox, Panel, StackPanel, Viewbox, WrapPanel

WPF는 여러 컨트롤들을 그룹화하여 관리하기 위한 여러 가지 컨트롤들을 제공하고 있다

[표29-1] WPF의 기본 컨트롤

다음 [표29-1]에서 살펴본 컨트롤 말고도 WPF는 문서를 처리하기 위한 고급 컨트롤들을 제공하고 있고(DocumentViewer, FlowDocumentReader 등) 또한 타블렛 PC에서 유용한 잉크 API 그리고 다양한 대화 상자들을(PasswordBox, PrintDialog, FileDialog,OpenFileDialog, SaveFileDialog) 제공해 주고 있다.

FileDialog, OpenFileDialog, SaveFileDialog 클래스들은 PresentationFramework.dll 어셈블리에서 제공하고 있는 Microsoft.Win32 네임스페이스에 정의되어 있다.



만약 윈도우 폼을 다루다가 WPF를 다루게 되는 경우라면 아마도 기존의 윈도우 폼보다 제공되는 컨트롤이 약간 적다는 생각을 가질 수 있을 것이다. 하지만 WPF에서는 아주 쉽게 컨트롤들을 만들 수 있고 또한 커스텀 컨트롤이나 유저컨트롤로 프로젝트 별로 재사용하기에 용이하다는 것을 알아두자.

이번 책에서는 WPF 컨트롤 라이브러리들이나 커스텀 컨트롤의 구조에 대해서는 설명하지 않는다. 만약 이에 대해서 더 자세히 알고 싶다면 닷넷 프레임워크 3.5 SDK 문서를 참고하기 바란다.



 
WPF 컨트롤과 비주얼 스튜디오 2008

비주얼 스튜디오를 이용해서 WPF 응용 프로그램 프로젝트를 생성해보면 다음 [그림29-1]과 같이 그룹별로 정리되어 있는 도구 상자를 볼 수 있을 것이다. 

윈도우 폼 프로젝트처럼 이런 컨트롤들은 비주얼 디자이너 창으로 드래그가 가능하고 또한 속성창을 이용해서 설정이 가능하다. 그리고 28장에서 XAML 에디터를 이용해서 이벤트를 생성했던 것을 생각해보자. IDE는 자동으로 이벤트를 코드 파일에 적절히 생성해준다. 


[그림29-1] 비주얼 스튜디오 2008 도구상자

컨트롤에 대한 보다 자세한 내용

이번 장에서는 이 모든 컨트롤들을 살펴보지는 않을 것이다. 대신 컨트롤을 구성하고 있는 기본적이면서도 중요한 내용을 살펴볼 것이다. (의존속성, 이벤트 라우팅, 명령 등) 

만약 이러한 컨트롤들의 특정 기능들을 알고 싶다면 .NET Framework 3.5 SDK 문서를 읽어보는 것이 가장 빠를 것이다. 그리고 컨트롤 라이브러리에 대한 자세한 내용을 알고 싶다면 [.NET Framework Development]->[Windows Presentation Foundation]->[Controls] 을 찾으면 많은 정보들을 볼 수 있을 것이다. 
 

[그림29-2] WPF컨트롤에 대한 각각의 정보

이 SDK 문서를 이용하면 컨트롤의 모든 정보를 알 수 있을 것이고 또한 다양한 코드 샘플 그리고 상속 관계, 인터페이스, 적용된 애트리뷰트들과 같은 다양한 정보를 살펴볼 수 있을 것이다. 그럼 XAML에 선언되어 있는 컨트롤들에 대해서 간단히 살펴보도록 하자.


XAML로 선언되어 있는 컨트롤들

지금까지의 개발자들은 어느 정도 고정되어 있는 컨트롤들을 이용하여 UI를 개발해 왔다. 예를 들어 Label 컨트롤은 언제나 텍스트 정보를 가지고 있고 테두리는 없을 것이다. 그리고 버튼의 경우 회색 네모에 특정 문자열을 가지고 있고 이미지 또한 삽입이 가능할 것이다. 또한 어떤 특정 프로젝트에서는 커스텀마이징 된 컨트롤들을 요구하게 되고 개발자들은 종종 필요한 컨트롤들을 원하는대로 커스텀마이징 하여 제공하기도 했을 것이다. 

WPF에서 제공하는 컨트롤들은 마크업을 통해서 우리가 원하는 대로 변경할 수 있다. 뿐만 아니라 많은 컨트롤들이 content 속성을 통해 어떤 컨트롤이라도 쉽게 추가할 수 있게 설계되었다. 28장에서는 Content 속성을 명시적/묵시적으로 특정 개체들을 추가하는 예제들을 살펴봤었다.

그렇다면 비주얼 스튜디오에서 ControlReview라는 이름의 WPF 응용프로그램 프로젝트를 생성해 보도록 하자. 그리고 “모든 버튼들은 회색 배경의 네모모양으로 특정 문체를 가지고 있다” 라는 편견을 버리기 위해서 버튼 컨트롤의 Content에 다음과 같은 개체들을 추가해 보도록 하자.

<!-- 커스텀 버튼 만들기 -->
<Button Name="btnPurchaseOptions" Height="100" Width = "300">
    <StackPanel>
        <Label Name="lblInstructions" Foreground = "DarkGreen" Content = "Select Your Options and Press to Commit"/>
        <StackPanel Orientation = "Horizontal">
            <Expander Name="colorExpander" Header = "Color">
            <!-- 특정 아이템들이 있다고 가정하자 -->
            </Expander>
            <Expander Name="MakeExpander" Header = "Make">
            <!-- 특정 아이템들이 있다고 가정하자 -->
            </Expander>
            <Expander Name="paymentExpander" Header = "Payment Plan">
            <!-- 특정 아이템들이 있다고 가정하자 -->
            </Expander>
        </StackPanel>
    </StackPanel>
</Button>



<Button> 컨트롤은 세 개의 <Expander> 컨트롤을 가지고 있고 <StackPanel> 을 이용해서 각각의 리스트들을 정렬 시키고 있다. 이렇게 간단하게 원하는 버튼으로 정의하는 것이 가능하다는 것을 볼 수 있을 것이고 이 버튼을 실제로 실행해 보면 다음 [29-3]과 같은 모양을 볼 수 있을 것이다. 

 
[그림29-3] XAML을 이용해서 커스텀마이징 해본 버튼

윈도우 폼을 이용해서 이와 같은 버튼을 만든다고 생각해보자. 그럼 API 기반으로 수동으로 UI를 작성해야 할 것이며 다양한 이벤트 핸들러와 컨트롤들의 컬렉션들을 업데이트 해주는 등 여러 가지로 상당히 귀찮은 작업들이 많을 것이다. 

마크업이라는 개념이 등장하게 되면서 특정 이벤트를 설정하거나 커스텀 동작을 만들 경우를 제외하고 컴파일을 할 경우가 많지 않다. 만약 UI의 수정만 필요하다면 XAML만 수정함으로써 충분히 수정이 가능하다. 

코드에서의 Controls의 설정

이전 장에서는 WPF의 클래스의 속성들은 엘리먼트의 애트리뷰트를 이용해서 설정하는 것을 살펴봤을 것이다. 대부분의 경우 XAML 엘리먼트의 애트리뷰트들은 System.Windows.Controls 네임스페이스에서 제공하는 각각의 클래스들의 속성들과 이벤트들과 매핑이 된다. 그렇기 때문에 마크업과 코드 이 두가지 방법을 혼용하여 컨트롤들을 정의할 수 있는 것이다. 

만약 XAML의 엘리먼트에 정의되어 있는 Name이라는 엘리먼트를 이용하면 언제든지 XAML에 선언되어 있는 컨트롤에 접근이 가능하다.



앞에서 살펴본 XAML 마크업은 Name이라는 애트리뷰트를 할당해 주었고 코드에서 직접 접근이 가능할 뿐만 아니라 특정 이벤트를 선언하는 것 또한 가능하다. 예를 들어, FontSize의 값을 변경하고 싶다면 다음과 같이 이용할 수 있다.

public partial class MainWindow : System.Windows.Window
{
    public MainWindow()
    {
        InitializeComponent();
        // Label의 FontSize의 변경
        lblInstructions.FontSize = 14;
    }
}



 
이렇게 Controls의 Name을 통하여 특정 멤버 변수를 변경하는 것이 가능한 것은 *.g.cs라는 파일에서 자동으로 멤버 변수가 생성되기 때문이다. 

public partial class MainWindow : System.Windows.Window,
System.Windows.Markup.IComponentConnector
{
    // 마크업에 선언하였던 멤버변수
    internal System.Windows.Controls.Button btnPurchaseOptions;
    internal System.Windows.Controls.Label lblInstructions;
    internal System.Windows.Controls.Expander colorExpander;
    internal System.Windows.Controls.Expander MakeExpander;
    internal System.Windows.Controls.Expander paymentExpander;
    ...
}



만약 이벤트를 컨트롤에 추가해주고 싶다면 다음과 같이 XAML을 정의할 수 있을 것이다. 

<Button Name="btnPurchaseOptions"
Click="btnPurchaseOptions_Click"
Height="100" Width = "300">
...
</Button>



그리고 이 델리게이트를 기반으로 하는 메서드를 다음과 같이 코드로 정의할 수 있다. 

private void btnPurchaseOptions_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Button has been clicked");
}



 
물론 여기서는 XAML에서 이벤트를 지정했지만 XAML의 이벤트를 지우고 Window의 생성자에서 다음과 같이 Click 이벤트를 지정할 수 있다. 

public MainWindow()
{
    InitializeComponent();
    // Label의 FontSize의 변경
    lblInstructions.FontSize = 14;
    // 버튼의 Click 이벤트 설정
    btnPurchaseOptions.Click +=
      new RoutedEventHandler(btnPurchaseOptions_Click);
}



지금까지 기본적인 컨트롤 모델에 대해서 살펴보았다. 다음에는 WPF 컨트롤에 있어서 가장 중요한 의존성 속성(Dependency Property)과 이벤트 라우팅(Routed Event)에 대해서 살펴볼 것이다. 이러한 내용들이 일반적으로 WPF 프로그래밍 하는데 있어서 직접적으로 눈으로 드러나는 내용은 아니지만 WPF를 깊게 이해하기 위해서는 핵심적이고 또한 굉장히 중요한 내용이다.

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