Introduction

WPF를 이용해 MVVM패턴으로 개발할때 데이터는 DataBinding을 사용자 입력은 Command를 사용해 View와 ViewModel을 느슨하게 연결합니다. 이것만으로도 충분히 디자인 코드와 로직 코드를 효과적으로 나눌 수 있기 때문에 많이 사용되고 있지만 Command로는 처리하기 까다롭거나 Command를 제공하지 않고 이벤트로만 제공되는 기능을 처리할 때(ex. MouseEvent, SelectionEvent 등)는 View와 ViewModel의 경계가 모호해져 완벽한 분리를 하기가 어렵습니다.

(Trigger와 Action을 이용해 일부 EventTrigger와 InvokeCommandAction을 통해 Event를 Command로 전달 할 수 있지만, 각 Event에 대해 모두 Command를 만들어 줘야 하고 파라미터에 대한 정보 또한 전달 할 수 없기에 이방법은 이번시간에서는 다루지 않습니다.)

이번시간에는 MVVM으로 개발 할 때 ViewModel에 구현되어있는 Method를 Event, Command, Property에 바인딩 할 수 있는 MethodBinding을 구현하는 방법에 대해 소개합니다.

MethodBinding

MethodBinding과 DataBinding의 가장큰 차이점은 바인딩 하는 대상이 다릅니다. DataBinding은 Source객체의 Property를 바인딩하지만 MethodBinding은 Method를 바인딩 합니다. 또 전용 Provider를 통해 Method를 전달 해 주기 때문에 Target객체의 속성이 Delegate Property가 아닌 Event, Command형태로 있다하더라도 대응이 가능합니다.

아래와 같이 ViewModel이 정의되어있다라고 했을때

MethodBinding의 적용하면 아래와 같이 사용할 수 있습니다.
그리고 ViewModel은 아래와 같이 구성되었습니다.

Create MethodBindingExtension

MethodBinding은 크게 3가지 파트로 나뉘며 각 파트별 설명은 아래와 같습니다.

  1. MethodBinding Extension : MarkupExtension으로 MethodBindingProvider의 Proxy Method를 생성
  2. MethodBindingProvider : 대상 객체의 DataContext로부터 Method Path를 추출하여 메서드를 호스팅
  3. CommandProvider : 대상 Property가 Command형식 일경우 Method를 Command로 감싸는 역할

아래는 MethodBindingExtension의 MethodBindingProvider를 생성하고 ProxyMethod를 구현하는 코드입니다.

대상 Property의 Handler Type을 추출하고 HandlerType의 Parameter들을 object[] 파라미터로 변환한 뒤 MethodProviderHandler를 호출 하는 LamdaExpression을  생성합니다. 동적으로 Handler Type에 대한 Proxy Method를 다양한 형태의 Delegate를 처리 할 수 있습니다.

아래는 이번시간에 구현한 MethodBinding의 전체 소스코드이며 기타 궁금하신 점이나 문의 사항은 메일이나 댓글로 남겨주시면 답변드리겠습니다.

WIT.WPF.MethodBindingExample.zip

신고

Intorduction

WPF Application을 개발하다보면 시간이 흐를 수록 프로그램 규모가 점점 커지면서 프로그램 구동시간이 점점 느려지는 현상이 있습니다. 구동시간이 느린 원인으로는 크게 2가지가 있는데 ,

첫번째는 짐작하고 있듯이 MainWindow를 띄우기 전에 프로그램에서 필요한 여러가지 정보를 로드하기 위한 과정때문에고,
두번째는 WPF가 시스템에서 사용하기 위해 준비하는 과정이 느린 경우입니다.

로드할 데이터가 많다면 Splash Screen을 띄워놓고, 데이터를 비동기로 로드하는 방식을 통해 사용자로 하여금 기다릴 수 있는 UX를 제공해 줄 수 있지만, 여전히 WPF가 준비되는데까지 걸리는 시간을 해결하지는 못합니다. 그래서 이번시간에는 WPF에 비해 준비과정이 필요없는 Winform을 통해 가벼운 SplashScreen을 구성하는 방법에 대해 소개합니다.
(.NET 3.5에서 추가된 SplashScreen BuildAction의 경우 내부적으로 WPF기술을 사용하기 때문에 이 글에서는 제외되었습니다.)



코드를 살펴보면 총 3가지 특징이 있고, 각 부분별로 설명하면 다음과 같습니다.

WPF Application에서의 Main함수 

WPF Application에서는 기본적으로 App Class에 Main함수가 자동으로 구현되어 따로 Main함수를 만들어 줄 필요가 없습니다. Application 시작과 관련된 대부분의 코드는 App의 StartUp메서드를 Override하여 처리 할 수 있지만 App이 생성되면서 로드하게 되는 수많은 Assembly 로드과정을 SplashScreen이 출력된 이후로 미루고자 별도의 Main함수를 구현하여 사용했습니다. (추가로 구현한 Main함수는 프로젝트 설정에서 따로 선택할 수 있습니다.)

SplashImage를 가져오기 위한 GetManifestResourceStream

WPF에서는 Build Actino이 Resource인 데이터를 가져오기 위해 Application.GetResourceStream과 같은 메서드를 제공하고 있습니다. 이 메서드를 사용해도 결과적으로는 동일하지만 Application.GetResourceStream를 사용해 내부적으로 로드되는 어셈블리를 미루고자Assembly에서 데이터를 바로 꺼내올 수 있는 GetManifestResourceStream를 사용했습니다. (해당 리소스를 Embedded Resource로 설정해야 합니다.)

CompositionTarget.Rendering

WPF의 경우 기본적으로 전용 Renderer에 의해 화면이 Rendering 되기 때문에 DC에 직접 그리는 Winform과 달리 화면에 출력되는 시점을 정확하게 알 수가 없습니다. 다만 프로그램이 안정적으로 렌더링 되고 있다는 것을 확인 할 수 있는데, WPF 화면을 렌더링 하기전에 호출되는 CompositionTarget.Rendering 이벤트를 이용합니다.

WPF는 기본적으로 시스템에서 안정적으로 동작할 수 있는 FPS를 유지하게 되는데, 최소 30~60FPS를 유지 합니다. 그래서 Rendering 이벤트에 30FPS이상의 속도로 렌더링이 되고 있는 시점을 로드가 모두 완료되었다고 판단하고 SpalshScreen을 종료합니다.

첨부된 파일은 이번시간에 사용된 전체 소스코드이며 기타 궁금하신 점이나 문의사항은 이메일이나 댓글로 남겨주시면 답변드리겠습니다.

WIT.WPF.SplashScreenExample.zip

신고

Introduction

WPF Application을 개발할 때 XAML에서 값에 따라 속성를 바꾸거나 Animation을 시작하려고 할때 Trigger를 사용합니다. Trigger에는 종류가 여러가지가 있는데, 그중 DataTrigger는 Binding결과값에 따라 Setter를 처리하는 기능을 가지고 있습니다. DataTrigger는 기본적으로 Binding결과의 1:1매칭만 결과에 의존해 Setter를 처리하는데, 이렇게 되면 Enum데이터에 OR 연산으로 입력되어있는(Flag형태로되어 있는)데이터를 처리하기 위해서는 Converter를 사용해 처리하거나 DataTrigger부분을 두번 사용해야 합니다.

이번시간에는 DataTrigger를 이용해 Flag에 대응할 수 있는 FlagTrigger를 구현하는 방법에 대해 소개합니다.

Create FlagTrigger

FlagTrigger를 구현하기 위해 DataTrigger를 상속 받아보면 모든 Virtual Method가 Internal로 구현되어 있어 막상 개발자가 제어할 수 있는 부분이 없습니다. 하지만 다행히도 Binding객체에 접근할 수 있고 Binding 속성이 필수값이기 때문에 Converter와 Dispatcher를 적절히 사용하면 간단히 Custom Trigger를 구성 할 수 있습니다. 아래는 FlagTrigger의 전체 소스코드입니다.

위 코드에서 보는바와 같이 FlagTrigger는 크게 두부분으로 나뉘는데, 첫번째는 연결된 Binding객체에 내부적으로 처리할 ConverterWrapper를 적용해주는 부분. 그리고 두번째는 ConverterWrapper에서 결과를 처리하는 부분으로 나뉩니다.

Binding 객체의 기존 Converter를 ConverterWrapper로 교체하는 작업에서 Dispatcher를 사용한 이유는 생성자 호출시점에는 Binding객체가 존재 하지 않기 때문에 BeginInvoke를 통해 생성자 이후 시점에 다시한번 초기화 할수 있도록 유도하기 위합니다. 그리고 Binding 객체에 이미 Converter가 적용되어있을 수 있기 때문에 ConverterWrapper에서는 기존 Converter의 결과를 가지고 Flag를 위한 연산을 수행합니다.

실제 사용할 때에는 아래와 같이 Enum값을 구성을 하고,

XAML에서 아래와 같이 사용하시면 됩니다.

WidthOrHeight에는 Width와 Height가 OR연산으로 포함되어있기 때문에 Width, Height, WidthOrHeight 3가지 값에 대해서 Trigger가 동작하는것을 확인 할 수 있습니다. 아래는 이번시간에 사용된 전체 소스코드입니다. 기타 궁금하신 점이나 문의 사항은 댓글이나 메일 주시면 답변드리겠습니다.

WIT.WPF.FlagTriggerExample.zip

 

신고

 

Introduction

WPF에서는 효율적인 XAML개발을 위해 여러가지 MarkupExtension을 제공합니다. Binding이나 StaticResource, DynamicResource가 대표적인 인데요. 기본적으로 제공되지 않는 기능의 경우 직접 MarkupExtension을 개발 할 수도 있습니다. 이번시간에는 XAML단에서 Static Method에 접근이 필요한 경우를 위해 StaticMethod MarkupExtension을 구현하는 방법에 대해 소개합니다.

Create StaticMethodExtension


StaticMethodExtension의 구조는 WPF에서 제공되는 StaticExtension과 유사합니다. 다른점은 Member에 해당하는 부분이 Method로 대체되고 Reflection을 통해 Static Member에 접근하는 대신 Static Method에 접근하는 점 입니다. IXamlTypeResolver와 IProvideValueTarget를 통해 사용자가 입력한 Path에 대한 MethodInfo를 가져오고 Delegate.CreateDelegate를 통해 StaticMethod의 Delegate를 제공해주는 방식으로 구성됩니다.

아래와 같이 C#코드에 Static메서드를 정의한 뒤

XAML코드에서는 아래와 MarkupExtension을 사용할 수 있습니다.

이상이며, 기타 궁금하신 점있으시면 댓글달아주시면 답변드리겠습니다.

신고

Introduction

WPF에서 Binding결과값을 다른 값으로 변환하기 위해 Converter를 사용합니다. 일반적으로 Converter를 구현하기 위해 개발자는 IValueConverter를 상속받은 객체를 구현하고 리소스로 등록하여  사용합니다. 아래 코드는 ValueConverter를 이용해 Slider의 Value에 따라 TextBlock의 FontSize를 변환하는 샘플 코드입니다.


그런데 필요에 따라 Converter를 매번 구현하게 되면  필요 이상으로 Converter를 위한 Type 이 너무 많아지는 상황이 발생합니다. 이렇게 되면 Converter의 경우 XAML코드가 아니기 때문에 관리상으로도 어려움을 격게 됩니다.  이번시간에는 단순한 값 변환을 위해 ValueConverter를 이용하지 않고 XAML코드에서 처리가능하도록 하는 MarkupExtension을 구현하는 방법에 대해 소개합니다.

Create ValueSelectorExtension

ValueSelectorExtension을 구현하기 위해서 먼저 MarkupExtension 형식의 객체를 구현해야합니다. 이때 ValueSelector의 기준이 되는 값이 Binding결과가 될거기 때문에 간단히 Binding객체를 상속받고 ValueConverter또한 상속받는 구조로 아래와 같이 정의합니다.

ValueSelectorExtension은 Selector들을 통해 현재 값에 대한 Converter를 유동적으로 선택 할 수 있고 매칭되는 Selector를 통해 값을 변환 해주게 됩니다. 이제 여러가지 상황을 대응 할 수 있는 Selector만 임의로 구현하면 되는데요, Selector를 구현 할 때는 아래와 같이 구현 할 수 있습니다.

RangeSelector는 사용자가 입력한 범위에 따라 결과값을 지정해 줄 수 있습니다.  위와 같이 Selector를 구현하고 실제 XAML에서 사용할 때에는 아래와 같이 사용할 수 있습니다.


RangeSelector이외에도 용도에 맞게 BooleanSelector, TypeSelector 등을 구현한다면 많은 상황에 대응할 수 있는 Converter를 매번 만들지 않고도 대응 할 수 있습니다.  이상으로 이번 포스팅을 마치며 아래 첨부파일은 ValueSelectorExtension예제 파일입니다. 기타 궁금하신 점이나 문의사항은 이메일이나 댓글로 남겨주시면 답변드리도록 하겠습니다. 감사합니다.

WIT.WPF.ValueSelectorExample.zip

신고