Introduction

Application을 개발할때 목록에 항목이 추가되거나, 제거 되었을때와 같은 동적인 변경내용을 수신기에 알리기 위해 일반적으로 목록객체에 INotifyCollectionChanged를 상속받아 구현하거나  INotifyCollectionChanged가 구현된 ObservableCollection<T>등을 사용합니다. 동적인 목록의 변경에 대하여 즉각 반응하기 때문에 매우 유용하게 사용될 수 있지만, 많은 양의 목록이 추가/제거 될경우 매번 목록의 변동이 있을대마다 CollectionChanged이벤트가 발생하기 때문에 퍼포먼스에 크게 영향을 미칠 수 있습니다. 이번시간에는 많은 양의 아이템을 INotifyCollectionChanged가 구현된 목록에 추가/제거할때 CollectionChanged가 발생하는것을 방지하고 처리가 모두 끝난뒤에 호출되도록 하는 방법에 대해 소개합니다.

List<T> VS ObservableCollection<T>

위 동영상은 INotifyCollectionChanged가 정의되지 않은 List<T>와 INotifyCollectionChanged가 정의된 ObservableCollection<T>를 이용해 ListBox의 Item목록을 변경 했을때의 퍼모먼스 비교입니다. 데모에서는 동일한 갯수의 목록(30만 건)을 목록에 추가하고 ListBox를 갱신하는 과정까지의 시간을 측정했습니다.

결과는 INotifyCollectionChanged를 정의하지 않은 List<T>가 INotifyCollectionChanged가 정의된 ObservableCollection<T>보다 10배정도 빠른 결과를 나타내고 있습니다. 이유는 ObservableCollection에서 항목이 추가될때마다 CollectionChanged이벤트가 발생하고 이를 수신하는 ListBox에서는 매 항목이 추가될때마다 View를 갱신하고 있지만, List<T>는 항목이 추가되더라도 View를 갱신하지 않기 때문입니다. List<T>는 View에 영향을 미치지않기 때문에 목록추가가 완료된뒤 View의 ItemsControl에 Refresh메서드를 호출하는것으로 View를 갱신할 수 있습니다.

하지만, List<T>를 이용했을때가 항상 성능이 뛰어난 것은 아닙니다. 위에서 언급했듯이 List<T>는 INotifyCollectionChanged가 구현되지 않았기 때문에 목록에 아이템을 추가할때마다 직접 View의 ItemsControl을 Refresh 메서드를 호출해줘야하는데, ItemsControl의 Refresh메서드를 호출하게 되면 View를 다시 그리기 때문에 이는 더큰 부작용을 일으킬수 있습니다. 

Create SuspendObservableCollection

가장 좋은 방법은 작은 건의 항목이 변경될때에는 INotifyCollectionChanged를 이용해 해당 항목이 추가/제거 되었음을 알리고, 많은 항목이 변경될때에는 CollectionChanged이벤트 발생을 일시적으로 중단하고, 모든 항목의 변경이 완료되었을때 CollectionChanged를 발생시키는 방법입니다. 하지만, ObservableCollection의 경우 CollectionChanged 이벤트에 대한 중지여부를 직접적으로는 제어 할 수 없기 때문에 ObservableCollection을 상속받아 CollectionChanged이벤트 발생여부를 제어 할 수 있는 SuspendObserableCollection을 구현할 수 있습니다. 아래는 SuspendObservableCollection의 소스코드입니다.


ObservableCollection의 OnCollectionChanged에서 내부적으로 발생시키는 CollectionChanged이벤트 호출을 제어하기 위해 IsSuspend 속성을 구현해 CollectionChanged 이벤트의 호출을 제어 했습니다. 실제 적용될 때에는 다음과 같이 사용할 수 있습니다.


이제 한두건 정도의 간단한 목록 변경에서는 INotifyCollectionChanged를 이용해 변경을 알리고 많은건의 목록변경에서는 IsSuspend와 Refresh메서드를 이용해 CollectionChanged이벤트를 제어 할 수 있습니다. 아래 동영상은 위에서 실시한 퍼포먼스 비교를SuspendObservalbleCollection에도 적용한 내용입니다.

Reflection for INotifyCollectionChanged

추가로, Reflection을 이용해 SuspendObservableCollection을 구현하지 않고 CollectionChnaged이벤트에 대한 제어를 수행할수 있습니다. 아래 코드는 Reflection을 이용한 CollectionChanged이벤트 제어를  Extension Method로 구현한 코드는 입니다.


CollectionChanged를 제어하기 위해서 PasueNotifyCollectionChanged메서드와 ResumeNotifyCollectionChaged를 이용합니다. 퍼포먼스는 SuspendObservableCollection보다 다소 느리지만, Type을 수정할 수 없는 상황이거나, INotifyCollectionChanged을 구현한 다른 Type의 Collection에서 유용하게 사용할 수 있습니다.

 

신고

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을 구현하는 방법에 대해 소개해 드렸습니다.
신고
TAG : colorpicker, HSB, RGB, wpf, XAML

Introduction

이번시간에는 WPF를 이용해 색을 선택할 수 있는 간단한 ColorPicker를 제작하는 방법에 대해 소개합니다. ColorPicker는 WPF4.0에서 기본 컨트롤로 추가되었지만, WPF 3.0을 사용하시는 분이나 직접 ColorPicker를 제작해야하는 분들께 도움이 됬으면 하는 바램입니다. 위 동영상은 이번시간에 구현하는 ColorPicker 데모 동영상입니다.

Color Picker Color Model

Color Picker를 개발하기 앞서, ColorPicker의 Color Model에 대해 살펴보겠습니다. 위그림은 Expression Blend에서 제공하는 ColorPicker로 HSB(Hue, Saturation, Brightness) ColorModel을 이용하고 있습니다. 왼쪽 영역에서는 색의 명도와 채도를 선택할 수 있고, 오른쪽에서는 색상을 선택할 수 있는 구조입니다. Blend 뿐만아니라 Photoshop이나 기타 디자인 툴에서도 이와 마찬가지의 구조를 제공하고있는데요, 이유는 색을 기준으로 명도,채도로 구분 되었을때 직관적으로 선택할 있기 때문입니다.

다른 Color Model을 이용하는 경우에도 마찬가지로 색을 선택할 수 있는 ColorPicker를 구현할 수 있지만, 색의 기준이 되어 줄수 있는 색이 없기 때문에 사용자가 특정 색을 선택하고자 할때 많은 어려움을 겪게 됩니다. 위 그림은 Photoshop에서 제공되는 RGB ColorModel을 이용한 Color Picker로 원하는색(예를들어 파랑)을 선택하기 위해 어떻게 조절해야 하는지 한번에 와닿지는 않습니다.

HSB ColorModel의 경우 색을 기준 값으로 제공 할 수 있기 때문에 사용자가 원하는 색을 선택하는데 더 편리합니다. Color Model에 대한 자세한 내용은 http://en.wikipedia.org/wiki/Color_model 를 참고하시기 바랍니다.

Create HSB ColorModel Presenter

위에서 언급했듯이 일반적으로 ColorPicker에 사용되는 HSB ColorModel은 색,명도,채도로 구분됩니다. 그림으로 표현하면 위와 같이 표현할수 있으며 Hue를 빨간색으로 선택했을 때 Brightness는 빨강에서 검정으로, Saturation은 빨강에서 흰색으로 색이 변하는것을 알 수 있습니다. 이를 XAML로 표현하면 아래와 같이 표현할 수 있습니다.


위와 같이 Brightness와 Saturation을 표현했을 때 X축은 Saturation, Y축은 Brightness를 의미하기 때문에 쉽게 HSB Color를 계산할 수 있으며, 계산된 HSB를 다시 RGB로 변경하면 간단한 ColorPicker를 구현 할 수 있습니다.
신고


안녕하세요. 디자이너 채은석입니다.
요즘은 개발을 잘 모르는 디자이너와 디자인을 하기 힘든 개발자를 위해 보다 손쉽게 WPF UI 테마를 제작할 수 있도록 작업을 진행하고 있는데요, 일단 WPF에서 제공하는 기본적인 컨트롤들이 어떤 것이 있는지 파악하기 위한 작업부터 시작해 보았습니다. 그리고 그 후 GUI를 제작했는데 가장 보편적이고 무난한 Glossy스타일로 잡아 보았습니다.

* 아직은 본인이 익스프레션 블렌드에 익숙하지 못하기 때문에 먼저 포토샵으로 분위기를 잡아 보았습니다.


기본적으로 Normal, MouseOver, Press, Disable에 대한 정의를 내리고 개발자에게 넘겨 실제 WPF로 제작을 해본 결과는 다음과 같습니다. 기본적으로 Black이 메인으로 들어간 테마인데 손쉽게 여러 다른 컬러를 넣음으로써 분위기를 바꿔 볼 수 있습니다.


* 클릭하면 원본 크기

일단은 개발자와 호흡을 맞춰 보는 느낌으로 시작 했는데 더 고민하여 약 4~5개 정도의 테마가 완성되면 그 테마 안에서 각 사용자가 원하는 컬러를 넣어 봄으로써 매번 다른 느낌의 GUI스타일을 만들어 낼 수 있는 테마 에디터를 제작해 볼 생각입니다.
 


저작자 표시 비영리 변경 금지
신고