2018년 4월 23일 월요일

WPF - 달력 컨트롤 만들기

오랜만에 사용자 컨트롤을 하나 만드려다 보니 기억이 잘 안났다.
가지고 있는 책도 없고, 다시 한번 정리해야 했다.

System.Object
  System.Windows.Threading.Dispatcher
  ㄴ쓰레드에 대한 작업 항목 큐를 관리하기 위한 서비스를 제공함.
  System.Windows.Threading.DispatcherObject
  ㄴDispatcher와 연관된 개체 요소.
    System.Windows.DependencyObject
    ㄴDependency property 시스템에 참여하는 개체를 나타낸다.
      System.Windows.Media.Visual
      ㄴHit test, 좌표 변형 및 경계 계산 등의 WPF 렌더링을 제공하는 요소.
        System.Windows.UIElement
        ㄴ 외관을 갖고 입력 처리를 할 수 있는 개체에 대한 기본 클래스 (Layout + Input + Focus + Events)
          System.Windows.FrameworkElement
          UIElement + Data Binding + Data Template + Styles, ...
            System.Windows.Controls.Control
            ㄴ User Interface의 기본 클래스. ControlTemplate으로 외관을 정의한다. (FrameworkElement + Control Template + Background / Foreground + Font, ...)
              System.Windows.Controls.ContentControl
              ㄴ모든 유형의 컨텐츠를 하나의 Control 개체로 표현하는데 필요한 기능들이 구현된 클래스.
                System.Windows.Controls.UserControl
                ㄴ컨트롤을 만드는 간단한 방법을 제공하기 위한 클래스.
            System.Windows.Controls.ContentPresenter
            ㄴContentControl의 내용을 표시하기 위한 클래스.

WPF의 UI 요소에 대한 클래스 계층은 잘 정리되어 있긴 하지만 위처럼 꽤 복잡한 편이다. 완전히 새로운 것이 아니라 기존의 컨트롤을 조합해서 신규 컨트롤을 만드는 관점에서는 Control 또는 UserControl 레벨에서 출발하는 것이 좋을 것이다. 또는 ContentPresenter가 쓰일 수 있다.

어쨌든 달력을 만들어야 했다. WPF엔 DatePicker가 있긴 하지만 구글 캘린더와 같이 하나의 컨트롤에서 특정 날짜의 해야할 목록을 자유롭게 편집할 수 있는 컨트롤이 필요했다.

시작점은 ItemsControl. 반복된 컨텐츠를 어떤 틀 안에 담고자 할땐 ItemsControl이 좋다. 각각의 항목을 표현하기 위한 ItemTemplate과 여러개의 항목을 담기 위한 ItemsPanel만 정의해주면 된다.

<ItemsControl.ItemTemplate>
  <DataTemplate>
...

<ItemsControl.ItemsPanel>
  <ItemsPanelTemplate>
...

그리고 달력같이 정해진 규격 내에서의 반복이라면 패널 중 UniformGrid가 적합하다. 달력의 세로는 6행, 가로는 일월화수목금의 7열.
<ItemsControl.ItemsPanel>
  <ItemsPanelTemplate>
    <UniformGrid Rows="6" Columns="7"/>
...

ItemsTemplate엔 DockPanel을 이용해 날짜를 표시하기 위한 TextBlock을 Top에 두고, AcceptsReturn 속성을 True로 갖는 TextBox를 하나 놓는다. 그리고나서 오늘을 나타내는 속성, 주말을 나타내는 속성, 이번달을 나타내는 속성 등을 지정해 화면을 치장하면 된다.

몰랐었는데 UserControl 대신 Control + Style 조합으로 컨트롤을 만드는게 더 간편하더라. 뷰에 대한 클래스는 UserControl이 아니라 Control을 상속받아 구현하게 되는데 반드시 static 생성자에서 OverrideMetadata를 호출해 주어야 한다. 그리고나서 필요한 DependencyProperty와 Style을 정의한다.
<Style TargetType="{x:Type ... }">
  <Setter Property="Template">
    <ControlTemplate>
...


WPF를 사용하는 사람이 최근엔 거의 없겠지만 쓰면 쓸수록 WPF는 참 잘만들어진 UI 프레임워크라 생각한다. 그런데 진짜 쓰는 사람 없는듯.

댓글 없음:

댓글 쓰기