2 Results for 'style'

  1. 2008.08.20 Custom Control Helper (2)
  2. 2008.07.20 SilverlightDefaultStyleBrowser改 for Beta 2 (1)

실버라이트 2 베타 2에서 가장 중요한 업데이트는 바로 Visual State Manager(이하 VSM)의 지원이었죠. VSM은 실버라이트가 강조하는 개발자와 디자이너의 분리된 협업을 더욱 잘 지원하게 해주고 개발 생산성을 정말 엄청나게 향상시켰죠. 저 역시 베타 1때의 커스텀 컨트롤 만들었던 것과 지금을 비교하면 그저 눈물이 나올 뿐이에요.

그렇지만 아직 커스텀 컨트롤도 해결해야 할 문제가 몇 가지 있는데요, 그 중에서 실제 개발 할 때 가장 귀찮고 시간이 많이 걸리는 부분은 바로 코드에서 정의된 템플릿 파트나 Visual State Group 및 Visual State의 이름을 가지고 generic.xaml에 기본 템플릿을 정의해야 한다는 점이에요. 개발자와 디자이너가 '이름'이라는 수단으로 일종의 계약을 맺는거죠. 일단 개념 상으로는 굉장히 좋아요. 먼저 컨트롤이라는 부품의 역할과 필요한 요소를 먼저 정의한 후 그 정의에 부합하는 한에서 디자인을 마음대로 할 수 있으니까요.

그러나, 개발자의 입장에서 '이름' 즉, 문자열에 기반한 계약 관계란 귀찮고 불안하기 짝이 없는 물건이에요. 만약 프로젝트를 진행하다가 모종의 이유로 인해 이 이름을 바꿔야할 필요가 있다면? 특정 이름이 더 이상 사용되지 않는다거나 새로 추가 되었다면? 게다가 이런 일은 상당히 자주 발생하죠. 이런 상황에서는 대체로 개발자 해당 '문자열'을 수작업으로 검색해서 바꿔주는게 일반적이에요. 문제는 이것이 실수를 만들기 매우 쉬운 프로세스라는 것이에요. 코드만으로 구성된 프로그램에서는 이런 일이 발생하지 않죠. 왜냐면 만약 코드에서 어떤 변수의 이름을 바꾸거나 삭제했다면 컴파일러가 곧바로 그 사실을 알려주고 에러 메시지를 뿜어내니까요. 하지만 단순한 문자열은 그 내용을 바꿨는지 어쨌는지 아니 심지어 그 문자열이 무슨 뜻인지 조차 컴파일러는 알지 못하니까요.

이 문제는 과거 실버라이트 1.1의 사용자 컨트롤에서도 있었던 문제였어요. 요컨대 XAML에서 x:Name 어트리뷰트로 선언한 이름을 코드 비하인드에서 일일이 수작업으로 그 엘리먼트의 참조를 만들어야만 했었죠.

이 문제는 실버라이트 2 베타 1이 나오면서 해결되었어요. 바로 사용자 컨트롤을 생성하면 XAML 마크업과 코드 비하인드가 쌍으로 만들어지고 XAML에서 x:Name 어트리뷰트로 선언된 이름은 Xaml precompiler에 의해 자동으로 동일한 이름을 갖는 멤버 변수로 선언이 되죠.

[UserControl이란 사용자 컨트롤을 만들면 위와 같이 XAML 마크업과 코드 비하인드 외에 컴파일러가 자동으로 만들어주는 g.cs파일이 생성됩니다.]

이렇게 되어 사용자 컨트롤을 만들 때의 귀찮은 단순 노가다가 사라졌을 뿐만 아니라 디자이너가 자유롭게 XAML상의 x:Name어트리뷰트를 바꿔도 개발자는 그 사실을 빠르게 캐치 할 수 있게 되었죠.

불행하게도, 아직 커스텀 컨트롤에는 이러한 매커니즘이 적용되어 있지 않아요. 디자이너는 단지 generic.xaml에 컨트롤의 스타일을 XAML로 정의할 뿐이고 개발자는 단지 코드 비하인드에서 필요한 요소들을 수작업으로 참조하게 되죠. 마치 1.1에서 했던 그것처럼요!

휴... 사설이 길었죠? 네 바로 이런 이유로 Custom Control Helper를 만들어봤어요.

이 툴을 만들면서 굉장히 삽질을 많이 했는데요, generic.xaml과 커스텀 컨트롤의 DefaultStyle 적용 매커니즘은 굉장히 복잡하기 때문이죠. 그 옛날 사용자 컨트롤처럼 단순히 XAML 코드에서 x:Name으로 선언된 어트리뷰트를 가진 모든 엘리먼트를 멤버 변수로 1:1 매칭하기만 하는 코드와는 차원이 달랐어요.

먼저 일반적인 Custom Control을 만들 때 가장 필수적인 요소들을 뽑아 보죠.

  • Custom Control은 DefaultStyle로 자기 자신의 Type을 사용합니다. 즉, generic.xaml에 선언된 Style중 TargetType이 자기 자신의 Type과 일치하는 스타일을 선택합니다.
  • Custom Control에 포함될 VisualStateGroup과 VisualState의 이름을 internal 멤버로 선언합니다. 이 때 Custom Control이 이미 부모 Custom Control로부터 상속받았을 가능성도 있으므로 부모 Custom Control이 해당 멤버를 이미 가지고 있을 경우는 제외합니다.
  • Custom Control에 포함될 Template Part를 리스트업 합니다. 일반적으로 Template Part의 대상으로는 Custom Control의 RootElement의 Resources에 포함된 Storyboard, 그리고 RootElement를 포함한 모든 하위 엘리먼트 중에서 x:Name 어트리뷰트를 선언한 엘리먼트를 의미하며 그 하위 엘리먼트들은 자기 자신의 Style 정의를 따로 갖을 수도 있으므로 이런 부분은 제외합니다. 마찬가지로 Template Part는 부모 Custom Control로부터 상속이 가능하므로 이미 선언 되었다면 제외합니다.
  • Custom Control을 위한 partial class 문자열을 생성합니다. 이 때 해당 클래스에 포함된 모든 Template Part들의 Type을 사용할 수 있도록 적절히 using 문도 넣어줘야 합니다.
  • Custom Control의 partial class는 InintializeTemplate이라는 멤버 메서드를 정의하고 그 메서드 내에서 스타일이 정의하고 있는 Tempalte Part들을 멤버 변수로 참조합니다. 이 때 해당 Template Part가 null이 아닌데 Type이 일치하지 않는 경우는 Assertion을 발생시키는 코드도 넣습니다.
  • Template Part 참조시의 Assertion 코드는 단순히 똑같은 코드를 반복할 수도 있고 클래스의 멤버 메서드로 묶어서 쓸 수도 있고 혹은 해당 클래스의 부모 클래스에서 이미 상속했을 수도 있습니다.
  • 컨트롤의 템플릿을 정의하는 XAML은 단순히 이름만 정의했을 뿐 실제로 코드에서 굳이 참조하여 사용할 필요가 없는 경우가 더 많습니다. 따라서 이름을 가지고 있는 엘리먼트 중에서 Template Part로 등록할 엘리먼트를 선택할 수 있어야 합니다.

...만들다 보니 진짜 복잡하네요...
여튼 이런 조건을 만족시키기 위해서는 XAML 만 분석해서는 답이 안나와요. 그래서 차선으로 선택한 것은 아예 컴파일 되어 있는 XAP 패키지를 직접 다운로드하고 그 안에 들어있는 어셈블리들을 동적으로 로드한 후 정확한 Type 분석 및 상속 관계를 파악하여 작업해야 했어요.

완벽한 형태가 되려면 Visual Studio 플러그 인으로 개발하면 더 좋겠지만 더 이상은 귀찮아서 손대기가 싫어요 -_-;

사용법은 간단하게 [Browse]를 클릭하여 XAP파일을 선택하세요. 주소창에 http://...처럼 웹에서 직접 선택하는 것도 가능해요.

선택된 XAP 파일내에 어셈블리들을 로드하고 각 어셈블리에 generic.xaml이 있을 경우만 리스트업하죠.

분석할 어셈블리를 선택하면 generic.xaml에서 스타일이 정의되어 있는 커스텀 컨트롤의 목록이 나오고 Options에서 Template Part를 참조하는 방법을 선택할 수 있으며 Required template parts에서 Template Part로 등록할 엘리먼트의 이름을 체크하면 돼요.

왼쪽 아래의 TextBox는 원본 generic.xaml 코드이고 오른쪽 TextBox는 현재 선택된 커스텀 컨트롤에 대한 partial class 코드에요. 이 코드를 복사해서 프로젝트에 새 파일로 추가하고 해당 클래스의 선언을 partial 로 변경해준 후 OnApplyTemplate 오버라이드 구현에서 InitializeTemplate()을 호출하면 돼요.

사실 이 샘플은 그냥 제가 쓸려고 만든거라 아마 별 도움은 안되겠지만, 혹시 XAP 파일에서 어셈블리를 얻어와서 분석하는데 관심이 있다면 소스코드가 도움이 될 거에요.


없을 걸 알지만 피드백 기대합니다. :)

신고
Posted by gongdo
boxmile 님과 이과장님이 알려주신 유용한 유틸리티, SilverlightDefaultStyleBrowser.
이게 없으면 실버라이트 스타일을 어떻게 했을까 몰라요.^^
무지 유용한 툴이니까 한번쯤 꼭 써보세요.



여튼 한가지 불편한 점은 스타일의 일부만 선택하고 Ctrl+C를 눌렀을 때 테스트 복사가 되지 않는다는 점이었는 데요, Ctrl+C로 클립보드에 복사되도록 코드를 수정해서 올립니다.
보너스로 각 엘리먼트 앞에 있는 접기/열기 표시인 -, +도 제거하도록 했어요.

신고
Posted by gongdo


티스토리 툴바