본문 바로가기

개발자국/WPF / Expression

Custom Panel in WPF

오늘 인도 엔지니어(라고 부르기엔...)가 짠 터치키보드 소스를 보았다. XAML엔 온갖 잡새가 날아들고, 비하인드 코드에는 제어문이 가득했다. 이걸 돈주고 짠거라니...

코드를 앞으로 F/U할 형도 울고, 나도 울고, PL도 울고 다 울었다.


그래서 나라면 어떻게 짤까 란 생각에 하이네캔 하나와 함께 코딩을 해보기로 했다.


일단 (탁구를 열심히 치느라 졸려서) 오늘은 Custom Panel에 대해서 정리해볼까 한다.


WPF에는 여러 컨트롤 그룹으로 구성되어 있다. 가령 입력을 받는 TextBox, 목록 형태의 ListBox, ComboBox, 그리고 자식 컨트롤의 배치를 담당하는 Panel 그룹이 있다.


이 글까지 검색해서 온 사람들이라면, WPF의 Grid, StackPanel, WrapPanel, Canvas 등과는 최소한 일촌일 거라 믿는다.


각각의 Panel은 특색이 있는데,

 - Grid는 영역을 세부 구역으로 나누고, 자식 컨트롤을 해당 구역에 배치한다.

 - StackPanel은 가로 혹은 세로로 자식 컨트롤을 (무식하게)쌓는다. 

 - Canvas는 자식 컨트롤이 원하는 위치, 크기에 맞춰서 그냥 대충 알아서 배치한다.


뭐 나머지는 찾아보시고..



일단은 요 위의 사진 처럼 키를 배치하는 Panel을 하나 만들까 한다. 그러기 위해서는 키보드 모양으로 자식 컨트롤을 배치하는 Panel을 하나 만들어야겠다.


자 그럼 먼저 내가 만들 Panel부터 선언을 하자.


여기에서 재정의한 두 함수는 Custom Panel을 만들기 위해서 중요한 함수이다.

 - MeasureOverride 함수는 자식 컨트롤을 돌면서 자식 컨트롤의 크기를 계산/Update하는 함수이다. 이때 MeasureOverride 함수를 제대로 안불로 주는 경우에는 자식 컨트롤의 크기가 이상해질 수 있다.

 - ArrageOverride 함수는 자식 컨트롤을 돌면서 배치하는 함수이다. MeasureOverride 함수를 통해서 크기가 계산되면 child.DesiredSize 속성이 변경되며, 이걸 이용해서 배치하는 것이다.


여기서 정말 중요한 점은 Panel을 상속받게 되면 UIElement 클래스에서 선언한 Measure, Arrange 함수가 보일 것이다. 하지만 이 친구를 재정의하면 안된다. 왜냐하면, 이 두 함수는 컨트롤의 크기를 계산하는 필수사항들이 구현되어 있기 때문이다. MSDN에서도 철저하게 금지하고 있다.(링크 참조)


일단 그럼 한번 대충 해보자. 먼저 MeasureOverride 다.

별 특이한 것은 없다. 여기에서는 현재 패널이 사용가능한 크기를 바탕으로 자식 컨트롤이 원하는 크기가 얼마인지 Update해주는 부분이다. 어차피 여기서 열심히 계산해 봤자, 키보드 특성한 모든 컨트롤 크기는 일괄적으로 같게 만들거다.


다음은 ArrangeOverride이다.

키보드가 첫줄을 10칸, 두째줄을 9칸, 셋째줄은 7칸이기 때문에 좀 배치가 지랄맞다. 일단 졸리기도 하고. 어쨋든 여기서 중요한 것은 foreach를 돌면서 각 child의 위치를 계산해서 child.Arrange 함수를 호출해주는 것이다.


여기서! 왜 child.Arrange에서 width와 height를 주는데 child.Width, child.Height를 변경할까?


그 이유는 Arrange에 주는 Rect의 width는 화면에 그려질 영역의 크기를 의미하기 때문이다. 즉, 컨트롤의 너비가 영역보다 크다면 화면에 짤려서 그려지기 때문에 width와 height를 다시 할당하는 것이다. 만약 Width를 다시 설정하지 않고, 자식 컨트롤의 크기가 영역보다 크다면, 아래와 같은 일이 벌어진다.

바보 같은 모습

사실, 자식 컨트롤의 크기를 자동으로 할당하게 한다면 전혀 문제될게 없기 때문에 선택사항이지 필수사항은 아니라는 것을 알아줬으면 좋겠다.



정상적인 모습

자 여기까지 Custom Control을 만드는 방법이다.



위에 처럼 각 버튼에 키를 매핑하는 코드는 아래와 같다. 귀찮으니까 코드만 적는다(라고 쓰고 캡쳐라고 읽는다).

이제 진짜 졸리다. 대충 짰다.