2017년 7월 9일 일요일

MVVM, Xamarin.Forms

Xamarin을 사용하게 되면서 MVVM에 대한 개념은 그래도 이해가 되긴 했지만
실제 코드에 적용하기에는 어려움이 있었던 관계로
링크와 함께 간단하게 대충 정리함....

보통 MVVM 검색하면 아래 그림과 link가 나와서 이것을 위주로 정리함.


https://msdn.microsoft.com/en-us/library/hh848246.aspx


그외 볼만한 link들

안드로이드의 MVC, MVP, MVVM 종합 안내서, Eric Maxwell
: https://news.realm.io/kr/news/eric-maxwell-mvc-mvp-and-mvvm-on-android/

우선 Xamarin 홈페이지에서도 XAML, Binding, MVVM에 대해서 자세히 설명하고 있다.
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_binding_basics/
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/



The MVVM Pattern


https://msdn.microsoft.com/en-us/library/hh848246.aspx

(이건 사실 Xamarin보다는 WPF, Silverlight 관련된 article이라 method나 xaml tag가 다른 부분도 있음.)

MVVM의 가장 큰 이유는 separation of concerns
Tightly couple될 수 밖에 없는 GUI와 logic을 분리하기 위함이다.

View와 Model은 View Model을 중간에 두고 서로 분리되어 있고
View는 View Mode간에서는 data binding과 notification을 사용해서 연동된다.

MVVM pattern은 명칭에서 알 수 있듯이 Model, View, View Model로 구성되어 있다.


View

View는 Screen에서 사용자들이 보는 GUI 부분이며 XAML과 code-behind 파일(.cs)로 구성된다. (물론 cs 파일로 구성될 수 도 있을 것이고..)
View는 각각 View Model을 가지고 View Model에게서 binding을 통해 data를 가져오거나 method를 호출한다.
run-time중 UI control이 변경되면 binding된 View Model의 property가 변경되어 View Model에서 적절한 처리를 할 수 있다.

View 파일 (XAML)
- Title, Label, Image, HtmlLabel, button이 binding 되어 있음.
https://github.com/hallower/WhooingNewsReader/blob/master/WhooingNewsReader/WhooingNewsReader/Views/ItemDetailPage.xaml

View code-behind 파일 (.cs)
- ItemDetailPage 두번째 constrctor에서
  ViewModel을 인자로 받아 BindingContext로 지정하고 있음.
https://github.com/hallower/WhooingNewsReader/blob/master/WhooingNewsReader/WhooingNewsReader/Views/ItemDetailPage.xaml.cs


Model

Model은 application의 domain model의 구현체로서 data model, business, validation logic을 포함한다.


View Model

View Mode은 View와 Model 중간 매개자 역할을 하며 View logic을 처리한다.
View Model은 Model의 method를 호출하여 model과 연동하고 전달 받은 data를 View에 맞게 가공한 뒤 View로 전달한다.
View Model은 UI의 event에 따른 action을 처리하기 위한 command를 제공한다.

View Model이 View와 two-way data binding을 하고자 할 경우 PropertyChanged event를 발생 시켜야 한다. 이를 위해서 View Model은 INotifyPropertyChanged inteface를 구현해야하고 실제 binding된 property가 변경 되었을 때 PropertyChanged를 호출하여 변경을 알려야 한다.

View Model 파일
- View에서 binding된 property들이 public으로 존재하는 것을 볼 수 있음.
- GetDetail에서 OnPropertyChanged()를 호출하여 View를 업데이트 하는 것을 볼 수 있음.
https://github.com/hallower/WhooingNewsReader/blob/master/WhooingNewsReader/WhooingNewsReader/ViewModels/ItemDetailViewModel.cs

Connecting View Models to Views


동작, 이벤트 처리를 위해 MVVM의 data binding을 사용하여 View와 View Model을 연결해야 한다. View Model이 연결되므로 view의 code-behind에는 business logic이 있을 필요가 없다.

Code-behind
View의 Code-behind file의 생성자에서 View Model을 DataContext(Xamarin에서는 BindingContext)로 View Model을 지정한다.

View
View Model가 default constructor 만 가지고 있을 경우 View에서 DataContext(Xamarin에서는 BindingContext)로 View Model을 연결 할 수 있다. 일반적으로 View Model Locator를 사용하는데 이는 각 view가 binding할 View Model을 제공하는 것으로 application이 여러 View들을 하나의 View Model로 연결하여 관리할 수 있다.



그리고 개발을 하다 보니

- View, View Model은 되도록이면 한 쌍으로 구성하는 것을 권장
 : 하나의 View Model이 여러 View를 관리할 경우 비대해질 경우가 있음.

- 단, 여러 View에서 동일한 Context가 공유 될 경우 View Model을 Singleton이나 만들어 사용 하는 것도 방법이다.

- View Model이 List로 구성되거나 중첩된 control/view로 구성된다면 View Model간 연동이 고민인데
 : View Model간 notification을 줄 수 있는 interface를 구현하거나
 : Xamarin의 Messaging Service를 사용하는 것도 방법이다. (편리하지만 남용은 금물이다.)

- View Model은 View의 초기화 시점에 새로운 instance로 만들어 진다.
 : 같은 View가 여러 번 보여지더라도 생성되는 View Model은 각기 다른 instance임.

- View Model의 Property들은 당연히 public 접근자를 가져야 함.

- OnPropertyChanged 구현 시 Property name의 default 값을 CallerMemberName으로 지정하는 것이 바람직함.
 : Property 이름 오타로 인한 notification 누락을 방지할 수 있음.

protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
      var changed = PropertyChanged;
      if (changed == null)
          return;

      changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

- Property 값을 private 변수로 만들어 두고 Property setter에 OnPropertyChanged를 호출하게 하는 것도 방법임.

private string displayText;
public string DisplayText
{
     get
     {
         return displayText;
      }
      protected set
      {
          if (displayText != value)
          {
              displayText = value;
              OnPropertyChanged();
          }
      }
}

- Two-way binding 시 View에서 Property 변경 시 다른 작업을 수행하려면
   BindablableObject의 PropertyChangedEventHandler를 사용하면 된다.

댓글 없음:

댓글 쓰기