요즘 .Net Framework 4.0 beta가 나와 관련 리뷰가 블로그 여기저기서 쏟아 지고 있다. 상황이 이렇다 보니 차기 WPF의 기능에 대해서 관심을 가지지 않을 수가 없다. What's New in the .NET Framework 4 페이지로 가보니 "In the .NET Framework 4 Beta 1, Windows Presentation Foundation (WPF) contains changes and improvements in many areas. This includes controls, graphics, and XAML." 라고 쓰여있는 것을 확인 할 수 있었다. 기대심에 What's New in Windows Presentation Foundation Version 4 페이지로 가보았다. Control, Multi-touch, Esing Function, Designer에 대한 링크와 짤막한 글이 포함되어 있었는데… 어찌된 일인지 내가 가장 궁금해했던 "XAML"에 대한 부분만 쏙 빠져있다. 서.. 설마 이게 끝은 아니겠지.. MSDN 쓰는 사람이 조금 바쁜가보다. 하고 생각해보았다.
이 적절치 않은 시점에 작년 PDC 2008에 리뷰된 차기 XAML의 기능을 살펴 보았다. 해당 내용은 Microsoft .NET Framework: Declarative Programming Using XAML 동영상으로 확인 할 수 있다. 참 오래된 이야기이다.
1. Easy Object References with {x:Reference}
2. Built-in Types
3. Generics in XAML with x:TypeArguments
4. Support for Arbitrary Dictionary Keys
5. Use of Non-Default Constructors with x:Arguments
6. Use of Static Factory Methods with x:FactoryMethod
1번 {x:Reference}이다. 이 Markup Extension은 반드시, 꼭, 꼭 필요하다. 빨리 빨리 하루빨리 추가 해다오.
<Button Name="button" />
<Label Target="{Binding ElementName=button}"/>
위 코드는 Label의 Target Property에 button이름으로 XAML상에서 정의된 button이라는 객체의 reference를 넣는 구문이다. … 그런데 정말 그런가? 사실 이미 정의된 객체의 reference를 Target Property에 넣는 단순한 작업을 하기 위해 위 코드는 복잡한 일을 하고 있다. 그 이유는 Binding이라는 Markup Extension이 반환하는 객체는 button 객체의 reference가 아니라 BindingExpression이기 때문이다. 물론 Path가 지정되어있지 않기 때문에 결국 reference가 반환되는 것과 동일한 효과를 가지지만 좀 떨떠름하다. 그리고 더 중요한 이유가 있으니 바로… Binding MarkupExtension 이 반환하는 것이 BindingExpression 객체 이기 때문에 이 구문을 사용할 때 Target은 반드시 의존 프로퍼티여야한다는 것이다. 이 사실에 얼마나 분개해 왔던가. 왜 진작 {x:Reference}와 같은 것을 MS는 생각하지 못하고 이제서야 추가한단 말인가. 하여간 추가 해준다니 감지덕지할 뿐이다.
사족 :: 제가 {x:reference}에 대한 필요성을 가장 크게 느낄 때는 Converter의 ConverterParameter로 xaml에 정의된 element의 참조를 넘겨 주고 싶을 때, 그리고 새롭게 정의한 MarkupExtension의 Constructor Parameter로 xaml에 정의된 element의 참조를 넘겨주고 싶을 때였습니다. 그리고 오늘 제 옆자리에 계신 분도 이 키워드의 필요성을 강하게 느겼다고 합니다.
2번이다. Built-In Type이다. 이젠 불필요하게 mscorlib의 System namespace를 xmlns 에 추가해주지 않아도 XAML namespace를 이용해 다양한 Type을 곧바로 사용할 수 있다. 제법 많구나.
<x:Object/><x:Boolean/><x:Char/><x:String/><x:Decimal/><x:Single/><x:Double/><x:Int16/><x:Int32/><x:Int64/>
<x:TimeSpan/><x:Uri/><x:Byte/><x:Array/><x:List/><x:Dictionary/>
Element 뿐 아니라 MarkupExtension도 허용해 주었으면 좋겠다. 예를 들어 Button의 Content에 Int32 Type의 12라는 value를 넣고자 한다면
<Button>
<x:Int32>12</x:Int32>
</Button>
이것 뿐 아니라 다음과 같이 사용할 수도 있도록 해주면 더 좋겠다.
<Button Content="{x:Int32 12}"/>
3번 Generics in XAML 이녀석도 엄청나게 유용할 것 같다. 다음과 같이 사용할 수 있단다.
<ObservableCollection x:TypeArguments="{x:Type Employee}">
<l:Employee FirstName="John" Name="Doe" />
<l:Employee FirstName="Tim" Name="Smith" />
</ObservableCollection />
위 녀석은 다음코드와 같은 의미를 지니게 된다. 벌써 부터 기대된다. 흐흐흐
var collection = new ObservableCollection<Employee>();
collection.Add(new Employee{FirstName="John", Name="Doe"});
collection.Add(new Employee{FirstName="Tim", Name="Smith"});
4번 Arbitrary Dictionary Keys string이 아닌 다른 Type의 Value도 x:Key에 지정 할 수 있다는 것인데 이것은 PDC에서 다음과 같은 예를 통해 설명되었다.
<StreamGeometry>M 0 0 L 12 8 l 9 12 z
<x:Key><x:Double>10.0</x:Double></x:Key>
</StreamGeometry>
사실 x:Key로 string이 아닌 Value를 사용할 수 있다는 것 보다 x:Key 키워드를 Element로 사용할 수 있다는 사실이 재미있다.
5번 x:Arguments 이 기능도 유용하게 사용될 수 있을 것 같다. 이 기능으로 인해 XAML 내에서 default constructor를 가지지 않는 Object도 정의할 수 있다. 정말 감사할 뿐이다..
<DateTime>
<x:Arguments>
<x:Int64>100</x:Int64>
</x:Arguments>
</DateTime>
여러 개의 Arguments를 사용하는 경우도 당연히 허용되어야 할 것이다. 이는 argument 라는 단어에 "s"자가 붙은것으로 봐 당연히 허용 될 것으로 보인다. 그리고 MarkupExtension도 꼭 만들어 줬으면 좋겠다.
6번 x:FactoryMethod 이에 대한 예는 다음과 같다.
<Guid x:FactoryMethod="Guid.NewGuid" />
이는 Guid guid = Guid.NewGuid();와 동일한 의미를 가진다. x:FactoryMethod는 그 단어 대로 static factory 메소드를 호출하게 사용된다. 그런데 MarkupExtension이 추가되지 않는한 Property지정에 사용할 수는 없을 것 같다. 만약 MarkupExtension이 있다면 다음과 같이 사용될 수 있을 것이다.
<local:MyObject Guid="{x:FactoryMethod Guid.NewGuid}"/> --> 이건 안되는 코드. 단지 바램일 뿐.
음.. 위와 같이 사용 가능하다면 내친김에 static factory 메소드 뿐아니라 그냥 static method도 범용적으로 사용가능하게 만들면 더욱 더 좋을 것 같다.
위가 XAML가 PDC 2008에 발표된 XAML namespace에 추가된 키워드를 소개한 것이다.
또 괜찮은게 있는데 훨씬 더 다양한 기능을 제공하는 XamlReader이다. 기존의 XamlReader가 Load, Parse 기능만 가지고 있던 것을 생각하면 이건 정말 훌륭하다. 모르긴 몰라도 XamlReader를 사용한 다양한 응용들이 Web상에 돌아다니게 될 것이다.
이에 추가로 몇가지 바램이 있다.
1. Style을 지정할 때 Base 클래스의 Target Type지정으로 Base 클래스를 상속받는 객체들의 Style이 지정되게 해달라.
<Style TargetType = "{x:Type Control}">
<Setter Property="Background" Value="Black"/>
</Style>
위 와 같은 경우 XAML내에서 Control을 상속받는 모든 Element가 이 Style이 적용 되게끔 해줬으면 좋겠다.
2. DataContext의 Type지정으로 Binding의 Source Type에 따라 Path를 정의할 때 CompileTime에 에러체크가 가능하게 해달라.
Binding이 유용하긴 하지만 CompileTime에 Path에 올바른 값이 들어갔는지 확인 할 수 없고 런타임에도 Exception을 먹어버린다.
즉 <Button Content="{Binding Path=오타}"/>라고 했을 때 Binding 의 Source로 지정된 객체(이 때는 Button 객체의 DataContext가 될 것이다.)에 "오타"라는 Property가 존재하지 않음에도 CompileTime과 심지어 Runtime에도 이 사실을 알 수 없다는 것은 조금 아쉽다.
x:TypeArguments 키워드도 생겼는데 이왕 Generic Binding 객체도 만들어 버려서 이를 사용할 경우에는 CompileTime이나 Design Time에 Path를 검사해 에러나 Exception을 발생시켜주면 바랄 바가 없다.
<Button Content="{Binding x:TypeArguments={x:Type local:MyObject}, Path=오타}"/>
3. Resource내에서도 MarkupExtension을 선언할 수 있게 해달라.
음.. 이건 좀 힘들다는 것은 알고 있다. 그래도 이게 되면 많이 유용할 것 같은데 어떻게 좀 해주면 안될까?
<Page>
<Page.Resources>
<Binding Path="PropertyName" x:Key="myBinding"/>
</Page.Resources>
</Page>
위와 같이 Resource에서 Binding을 미리 정의해놓고 코드에서 불러 사용 할 수 있으면 참 좋을 것 같다. 물론... 이건 힘들다는 것을 알고는 있지만 좀 고려해달라. 아니면 MarkupExtension이 아닌 Binding 클래스 비슷한 녀석을 만들어 주던지.
사족 -- Binding 은 MarkupExtension이기 때문에 위 코드는 다음과 같은 의미가 된다. 따라서 사용 불가능하다.
page.Resource = new Binding{ Path="PropertyName}.ProvideValues();
4. Visual Studio Designer는 x:Class로 지정된 partial class가 존재할 때 최소한 생성자 만이라도 읽어 줬으면 좋겠다. 그리고 해당 Project에 App.xaml이 있다면 해당 xaml의 Resource도 좀 알아서 읽어줬으면 좋겠다.
<Page x:Class="…">
<Button Content="{StaticResource 리소스키}"/>
</Page>
위 경우 당연히 지금 이 상황에서는 Error이다. 그러나 디자이너 넌 모르고 있다. InitializeComponent로 이 XAML 파일이 로드되기 전에 behind 코드에서 "리소스키"라는 key로 특정 객체가 Page의 Resource에 추가되어 있을 수도 있다는 것을...
5. 왜 유용한 기능을 가지고 있는 Class들은 모두 internal로 되어있는 것인가? 특히 System.Windows.Markup namespace의 많은 Class들을 사용할 수 있게 해주었으면 좋겠다.
…
…
이상하다 또 많은 불만 사항이 있었던 것 같은데… 생각날 때마다 추가해봐야겠다.

