Introduction

WPF를 이용해 MVVM패턴으로 개발할때, ViewModel에서 View에 있는 객체에 Binding을 해야 할경우, ViewModel에서 View에 있는 객체를 직접 Access해야 하므로, View와 ViewModel의 독립성이 떨어지게 됩니다. 이번시간에는 이러한 상황에서 MarkupExtension을 이용해 View에서 ViewModel의 독립성을 유지한체 ViewModel에서 View를 Binding할 수 있도록 하는 ReverseBinding을 구현 하는 방법에 대해 소개합니다.

Create DemoView(Default Data Binding)

ReverseBinding를 구현하기에 앞서 ReverseBinding을 테스트 하기 위한 상황을 만들기 위해 위와 같이 두개의 RadioButton이 있는 간단한 DemoView를 구현했습니다. 그리고 RadioButton에 ViewModel에 정의된 IsMale속성을 Binding하고, 텍스트 박스에도 IsMale 속성을 Binding합니다. 아래는 DemoView에 해당하는 소스코드입니다.


대충 눈으로 살펴봤을때는 특별하게 문제가 없는 코드 같지만, 실제로 동작을 시켜보면 동영상과 같이 DataBinding이 정상적으로 동작하지 않는것을 확인 하실 수 있습니다.
 
이유는 RadioButton의 특성상 IsChecked 속성이 변경될때 같은 Group내에 포함된 다른 RadioButton들의 IsChecked속성이 영향을 받게되는데, 이 과정에서 Binding되어있던 IsChecked속성에 다른 값이 덮여 씌워지면서  Binding이 해제되어 버리기 때문입니다.
 
이를 해결하기 위한 RadioButton에 Binding을 하지 않고, 아래와 같이 IsMale속성에 RadioButton의 IsChecked속성을 Binding하는 방법을 사용 할 수 있습니다.
하지만 위에서 언급 했듯이 ViewModel에서 View 객체를 멤버를 직접 제어하는 방법은 독립성이 떨어지기 때문에 View와 ViewModel에 종속받지 않고 위와 같은 동작을 수행하는 ReverseBinding을 직접 구현해서 사용 할 수 있습니다.

Create ReverseBinding Extension

아래는 ReverseBinding을 구현한 소스코드의 일부분으로, 요약하면 Binding객체를 파라미터로 받는 MarkupExtension을 생성하고, ProvideValueTarget을 이용해 Source Object와 Source Property를 가져온 뒤, DataContext의 Target Property에 Binding을 수행하는 과정을 나타냅니다.
위와 같이 구현된 ReverseBinding을 이용해 실제 XAML에서 사용할 때에는 아래와 같이 사용할 수 있습니다.


실행시켜보면 아래 동영상과 같이 Binding이 정상적으로 동작하는것을 확인 하실 수 있습니다.


아래는 이번시간에 사용된 전체소스코드입니다. 혹시 글을 읽으시고 이해가 안되시는 부분이나, 기타 문의사항은 댓글이나 이메일로 남겨주시면 답변드리도록 하겠습니다.

신고

Introduction

이전 시간에서 이야기한 바와 같이 포토샵을 사용하는 디자이너와 협업하여 WPF프로젝트를 진행하다보면, 포토샵을 사용해 디자인을 하는 디자이너의 의도를 WPF의 기본 기능만을 활용해서 수용하기란 한계가 따릅니다. 이번시간에는 포토샵을 이용해 디자인을 한 디자이너가 Gradient을 편집할 때 사용하는 Angle속성을 WPF에서 사용하는 방법에 대해 소개합니다.

What is the Gradient Angle?

Linear Gradient에서의 Angle이란 Gradient의 방향을 의미 하며, Angle을 변경하게 되면, 원의 중심을 Gradient의 시작점으로 하고 원의 끝을 Gradient의 종료지점으로 하는 Gradation이 생성됩니다. 포토샵에서는 위 그림과 같이 간단하게 각도를 입력하거나, 마우스를 이용해 각도를 조절할 수 있지만, WPF에서는 아래와 같이 시작점과 끝점을 이용해서 Gradation의 방향을 지정하게 됩니다.

시작점과 끝점을 이용하는 방법은 상황에 따라 더 디테일한 Gradation을 표현할 수 있기 때문에 유용하지만, 단순 각도만으로 방향을 지정해 주고 싶거나, 포토샵을 이용하는 디자이너와의 협업과정에서는 수작업으로 각도에 해당하는 시작점과 끝점을 지정해주어야 한다는점에서 번거러울수 있습니다.

Calculate Gradient Direction Using Angle 

각도를 이용해 Gradient의 시작점과 끝점을 계산하기 위해 수식을 이용해서 직접 계산해내는 방법이 있지만, 간단하게 WPF에서 제공하는 RotateTransfrom을 이용하면 쉽게 계산해낼 수 있습니다. 아래는 RotateTransfrom을 이용하여 Gradient의 시작점과 끝점을 계산하는 코드입니다.


WPF 에서는 Brush자체의 Transfrom에 대해 지원하기 때문에 직접 Transform을 적용 할 수 있지만, 위와 같은 방법을 이용해 직접 Start Point와 End Point를 지정해주면 Rendering Time에 매번 Transfrom을 위한 연산을 절약 할 수 있습니다. 실제로 XAML에서 사용할 때에는 아래와 같이 이용할 수 있습니다.


아래는 이번시간에 사용된 전체 소스코드입니다. 이 글을 읽으시고 궁금하신점이나 기타 문의 사항은 리플로 남겨주시면 답변드리도록 하겠습니다.

신고

Introduction

포토샵을 사용하는 디자이너와 협업하여 WPF프로젝트를 진행하다보면, 디자이너가 작업한 결과물들을 사용가능한 형태(XAML)로 변환하는 과정이 필요한데, 전문 디자인 툴인 포토샵의 기능들을 기본적으로 제공되는 WPF의 기능만으로 표현해 내는것은 한계가 따릅니다. 대표적인 예로 GradientBrush, Effect, Blend Mode등이 대표적인 예입니다. 이번시간에는 포토샵에서 Gradient를 편집할때 사용 할 수 있는 MidPoint GradientStop을 WPF에서 적용 할 수 있는 방법에 대해 생각해보고 구현하는 방법에 대해 소개합니다.

What is the MidPoint GradientStop?

MidPoint Gradient란, 시작 GradientStop에서 종료 GradientStop까지 색이 변화 하는 과정에서 두 색이 절반씩 섞여 중간색으로 변환되는 시점을 말합니다. 실제 포토샵에서는 예를 이용해 조금더 설명을 하자면, 아래 그림의 경우 빨간색을 시작으로 파란색으로 끝나는 Gradation에서 MidPoint가 0.5 즉, 빨간색과 파란색의 중간색인 보라색이 0.5지점에 위치하는걸 확인 할 수 있습니다.


마찬가지로 MidPoint가 0.9에 위치하게 되면, 아래 그림과 같이 중간색인 보라색이 0.9지점에 위치하게 됩니다.


간단한 논리로 생각을 해보면, 두개의 GradientStop을 지정했지만, 실제로는 MidPoint가 변경되거나, Start 혹은 End의 색이 변경될때 중간 값을 띄는 GradientStop이 하나 더존재 한다고 생각할 수 있겠습니다.

Create MidPoint Attached Property

WPF에서는 기본적으로 위에서 설명한 MidPoint를 지원하지 않기 때문에 MidPoint를 지원하도록 하기 위해 가장먼저 GradientStop에 MidPoint를 지정 할 수 있도록 아래와 같이 Attached DependencyProperty를 정의합니다. GradientStop에만 사용되기 때문에 대상 타입을 Gradient Stop으로 지정했습니다.

이제 MidPoint Attached Property가 정의되었으면, 위에서 이야기 한바와 같이 MidPoint와 관련된 일을 수행하는 GradientStop을 생성해야 합니다. 그리고 기준 GradientStop과 이전 GradientStop의 색에 따라 중간 색이 변경될 수 있도록 Binding을 하고, Converter를 이용해 중간색을 계산합니다.

Calculate Mid Point Color & Offset



위 코드는 두 GradientStop의 Offset을 입력받아 상대 값으로 입력된 MidPoint를 절대값 Offset으로 변환 하는 부분과 두 색을 입력받아 중간 색을 계산하는 코드입니다. 그리고 다음과 같이 XAML 에서 MidPoint를 적용하실 수 있습니다.



이번시간에 사용된 소스코드는 아래에서 다운로드 받으실수 있으며, 기타 궁금하신 점이나 문의 사항은 댓글달아주시면 답변 드리도록 하겠습니다. 감사합니다.

 
신고

Introduction

WPF Application을 개발할때 동적인 리소스를 활용하기 위해 DynamicResource를 이용합니다. 동일한 내용의 리소스를 메모리에 여러번 올리지 않기 때문에, 메모리 효율이나 퍼모먼스면에서 좋은 효과를 낼수 있습니다. 하지만, DynamicResource의 경우 XAML상에서 한번 지정하면(혹은 지정하지 않으면) 런타임상에서 지정이 불가능하기 때문에 당혹스러울 때가 많습니다.

MSDN에서는 이러한 상황을 위해 FrameworkElement수준에서 SetResourceReference를 제공하고 있지만, FrameworkElement가 아닌 DependencyObject 수준에서는 역시나 제어가 불가능 하기 때문에 완전한 해결방법이라고는 할 수 없습니다. 대표적인 예로 Brush의 경우 DependencyObject에서 Freezable, Animatable을 거쳐 파생된 클래스이기 때문에 SetResourceReference를 이용한 Runtime상에서의 DynamicResource를 지정할수 없습니다.

이번시간에는 Runtime상의 DependencyObject 수준에서 DynamicResource를 지정하는 방법에 대해 소개합니다. 위 동영상은 이번시간에 사용되는 데모 동영상입니다.

DynamicResourceExpression

먼저 우리가 XAML에서 DynamicResource를 지정하고 해당 Property에 DynamicResource가 지정되는 과정을 간략하게 표현하면 위 그림과 같이 표현 할 수 있습니다. XAML에서 DynamicResource MarkupExtension을 이용해 DynamicResource를 정의하면 DynamicResourceExtension객체가 생성되고 DynamicResourceExtension의 ProvideValue()를 통해 Dependency Property에 지정 가능한 ResourceReferenceExpression이 생성됩니다. 생성된 Expression은 최종적으로 DependencyObject의 SetValue메서드를 통해 해당 Property에 DynamicResource Reference가 생성되게 됩니다.

DependencyObject에 SetValue를 통해 Expression이 입력되는것을 확인 하고, 직접 ResourceReferenceExpression 객체를 입력하기 위해 ResourceReferenceExpression 생성하려고하면 문제가 발생합니다. ResourceReferenceExpression 가 상속받은 Expression 클래스의 경우 WPF 인프라를 지원하기 위해 제작된 클래스로, 사용자 코드에서 직접 엑세스가 불가능하기 때문입니다.

그럼 한단계 돌아가서 DynamicResourceExtension을 이용해 ResourceReferenceExpression을 생성하는 방법을 이용해 보겠습니다. DynamicResourceExtension의 경우 MarkupExtension을 상속받은 클래스기 때문에 사용자코드에서 생성하고 ProvideValue 메서드 또한 호출이 가능합니다. 아래 코드는 DynamicResourceExtension을 이용해 ResourceReferenceExpression을 생성하고 대상 DependencyObject에 Expression을 지정하는 코드입니다.


일반적으로 ProvideValue를 호출하기 위해 파라미터로 IServiceProvider를 제공해야하지만, 다행히도 DynamicResourceExtension의 경우 내부적으로 IServiceProvider를 제공하지 않더라도 사용가능하기 때문에 파라미터에 null을 입력하는것으로 Expression을 생성할 수 있습니다. 

실제 코드에서 사용할 때에는 위와 같이 ExtensionMethod를 이용해 Behid Code에서 DependencyObject에 DynamicResource를 지정해서 사용할 수 있습니다. 첨부파일은 이번시간에 사용한 데모 프로그램의 소스코드입니다.

신고

Introduction

MVVM 패턴등을 이용해 Application을 개발할때 View에서 호출되는 RoutedEvent를 처리하는데 있어 몇가지 이슈가 발생하게 됩니다. 일반적인 방법으로 RoutedEvent를 Command로 연결하기 위해서는 View와 ViewModel간의 종속적인관계가 유지 View와 ViewModel의 분리를 원칙으로하는 MVVM패턴을 이용할 떄 이 방법은 좋은 방법은 아닙니다. 이번시간에 소개할 내용은 이러한 MVVM 패턴을 이용해 RoutedEvent를 처리해야 하는 상황에서 View와 ViewModel간의 연결 없이 RoutedEvent를 Command로 연결할 수 있는 방법에 대해 소개합니다.

CommandBinding MarkupExtension

이번시간에는 MarkupExtension을 이용해 RoutedEvent와 Command를 연결할 수 있는 CommandBinding MarkupExtension을 구현합니다. 구현 완료시 아래 코드와 같이 XAML을 이용해 RoutedEvent를 쉽게 Command에 연결 할 수 있습니다.


CommandBinding MarkupExtension은 크게 2가지 부분으로 나뉩니다. 첫번째 부분은 CommandBiding MarkupExtension을 정의하는 MarkupExtension과 CommandBinding객체를 이용해 RoutedEvent와 Command를 연결시켜주는 역할을 하는 CommandBinding Attached Property부분입니다.

먼저 CommandBinding MarkupExtension은 아래와 같이 아주 단순한 구조로 Command와 RoutedEvent에 대한 정보를 저장할수 있는 형태를 갖습니다.


그리고 Attached Property에서는 입력받은 CommandBinding으로부터 Target의 RoutedEvent를 처리합니다. CommandBinding Property에서는 CommandBindingManager를 통해 CommandBinding MarkupExtension과 Target을 관리하고 Target의 ViewModel(DataContext)이 갱신되었을 때 CommandBinding에서 지정한 RoutedEvent를 Command에 연결하는 과정을 수행합니다.

이상 Reflection과 Attached Property, MarkupExtension을 이용해 CommandBinding을 구현하는 방법에 대해 소개해 드렸습니다.
신고