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

신고

Introduction

MVVM패턴을 사용할 때 일반적으로 ListBox에 목록을 바인딩 하기 위해 ObservableCollection<T>를 ItemSource에 바인딩하는 방법을 사용합니다. ObservableCollection의 경우 INotifyCollectionChanged를 상속받아 구현되었기 때문에 목록이 변경되었을때 마다 ListBox에 변경을 알려 ListBox의 View와 ObservableCollection의 목록이 동일하게 유지될 수 있도록 돕습니다.

그리고 ListBox의 현재 선택된 항목을 ViewModel에서 제어하기 위해 ListBox의 SelectedItem속성에 ViewModel의 속성을 TwoWay로 바인딩합니다. 하나의 선택된 항목을 관리하기 위해서 SelectedItem을 이용할 수 있지만, 만약 ListBox에서 MultiSelect를 지원하는 경우 이슈가 발생합니다.

ListBox의 SelectedItems속성의 경우 읽기 전용이기 때문에 바인딩을 할수 없기 때문입니다. 때문에 ViewModel에서 View에 해당하는 ListBox를 가지고 있어야 하는데 View와 ViewModel을 분리하는 MVVM패턴에서 ViewModel에서 ListBox를 직접 조작하는 방법은 좋은 방법은 아닙니다. 이번시간에는 Multi Select를 지원하는 ListBox의 SelectedItems속성을 ViewModel에서 View에 종속적이지 않고 SelectedItems를 동기화 하는 방법에 대해 소개합니다.

Attached Property & Weak Event Pattern

위에서 언급했듯이 ListBox의 SelectedItems는 읽기 전용속성이기 때문에 직접적인 바인딩이 불가능합니다. 따라서 ListBox의 SelectedItems을 모니터링하고, 동기화할 목록 지정하기 위한 Attached Property를 아래와 같이 구현합니다.

그리고 ListBox의 SelectedItems와 대상목록을 동기화 하는 SelectedItemsHelper를 구현합니다. 그리고 INotifyCollectionChanged를 상속받아 구현된 SelectedItems와 대상목록의 CollectionChanged 이벤트를 WeakEvent패턴을 이용해 수신할수 있도록 SelectedItemsHelper에 IWeakEventListener를 상속받고 구현합니다.

※ WeakEvent 패턴이란 이벤트를 수신하거나 제거될때 발생할수 있는 메모리 누수문제등을 위해 설계된 패턴으로 명시적으로 이벤트 수신기와 이벤트 관리자 클래스를 제공하여 이벤트수신기에 대한 수명을 직접관리하고 구조적으로 이벤트 처리를 독립시킬수 있습니다. AttachedProperty를 이용해 이벤트 처리와 관련된 기능을 구현 할 때 이벤트 수신/해제가 빈번하게 발생해 메모리 누수가 발생할수 있어 WeakEvent 패턴이용해 명시적으로 관리하면 효과적입니다.

SelectedItemsHelper에는 위와 같이 ListBox의 SelectedItems와 대상목록 중 한곳이 갱신되었을때 갱신된 내용을 다른 목록에도 반영시키는 것으로 두 목록을 동기화 합니다. 이렇게 하면 ViewModel상의 목록과 ListBox의 SelectedItems가 서로 같은 데이터를 유지할 수 있습니다. 아래 동영상은 Attached Property와 SelectedItemsHelper를 이용해 구현한 데모 프로그램의 동영상입니다.

위 동영상에서 사용된 ListBox는 아래 코드와 같이 ListBoxHelper.SelectedItems을 ViewModel의 목록으로 바인딩합니다.

화면 아래쪽의 버튼을 클릭했을때 발생하는 ViewModel의 Command는 아래와 같이 임의의 항목을 SelectedNumbers에 등록하는 기능을 수행합니다. SelectedNumber는 ListBoxHelper.SelectedItems에 바인딩 되어 있기 때문에 SelectedNumber의 목록을 갱신하는 것만으로 ViewModel에서 View에 종속적이지 않는 상태로 ListBox의 SelectedItems를 갱신할 수 있습니다.

아래는 이번시간에 사용한 데모 프로그램 소스코드입니다.

 

신고

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을 구현하는 방법에 대해 소개해 드렸습니다.
신고