본문 바로가기
Windows/C#/WPF

[WPF] 내 나름대로의 MVVM 요약 (수정. 210515_1836)

by hirudev 2021. 5. 15.

아무리봐도 도저히 이해가 안되서 오늘 하루종일 MVVM 이랑 씨름을 했다.

(작성하고보니 00시가 지나서 어제...)

 


MVVM 대충 요약

개요

일단, WPF 기준으로 View 에 해당하는 파일은 xaml 확장명을 갖는 파일들이고 (XML 코드들)

ViewModel 과 Model 은 cs 확장명을 갖는 파일들에 해당한다. (C# 코드들)

 

별도로, Data Binding 은 CLR Object 라고 적어두었는데, 데이터 바인딩 객체는 CLR 에서 관리하기에 사용자가 수정할 일은 없다.

(가져와써야할 일은 생길지 모르지만...)

 

특징

  • View 와 ViewModel 은 Data Binding 을 통해 서로 통신을 한다.
  • 바인딩이 된 View 와 ViewModel 은 서로 데이터가 동기화되어야 한다.
    (상황에 따라선 아닐 수 있다. OneWay, TwoWay, OneWayToSource 등 참고...)
  • MVVN 을 설계할 때
    View 는 ViewModel 의 존재를 알고 있지만 ViewModel 은 View의 존재를 알지 못한 채로
    ViewModel은 Model 의 존재를 알고 있지만 Model 은 ViewModel 의 존재를 알지 못한 채
    위와 같은 느낌으로 설계해야 한다. 그림으로 그리면 아래와 같다.

  • View 와 ViewModel 사이에는 둘의 데이터가 동기화되기위해 바인딩 클래스가 있어야한다.
    (WPF 가 아닌 다른 플랫폼이라면 데이터 바인딩이 되는 프레임워크가 필요할 것으로 보여진다.)

아주 간단하게 프로퍼티 정의

// MainWindow.cs

namespace TestApp {
    public partial class MainWindow:Window {
        InitializeComponent(); // 해당 코드는 생략
        DataContext = new ViewModel(); // DataContext 는 Window 객체에 존재.
    }
    public class ViewModel{
        public ObservableCollection<Model> Items{ get; set; }
        ViewModel() {
            Items = new ObservableCollection<Model>();
            Items.Add(new Model(){ Name = "홍길동"});
        }
    }
    public class Model{
        public string Name{ get; set; }
    }
}

위 코드에서 프로퍼티에 해당하는 것은

  • ViewModel 에서의 public ObservableCollection<Model> Items{ get; set; }
  • Model 에서의 public string Name{ get; set; }

위 2개의 코드가 프로퍼티에 해당한다.

 

프로퍼티에 해당하는 변수는 아래와 같이 바인딩으로 불러올 수 있다.

 

<!-- MainWindow.xaml -->

<Window ~생략>
   <ListBox DataContext="{Binding ViewModel}" ItemSource="{Binding Items}">
       <ListBox.ItemTemplate>
           <DataTemplate DataType="{x:Type Model}">
               <TextBlock Text="{Binding Name}" />
           </DataTemplate>
       </ListBox.ItemTemplate>
   </ListBox>
</Window>

위와 같이 풀어서 적었지만, 위 소스의 경우 아래와 같이 간략하게도 가능하다.

 

상황에 따라서 위와 같이 적어야할 때가 있다.

 

 

<!-- MainWindow.xaml -->

<Window ~생략>
   <ListBox ItemSource="{Binding ViewModel.Items}">
</Window>

 

 

INotifyPropertyChanged

View 를 바인딩하고 있고 View 안의 컨트롤을 조작하거나 값을 수정하거나 해야하는 상황이 발생하면

 

해당 클래스는 반드시 INotifyPropertyChanged 인터페이스를 상속하고 있어야 한다.

(따라서, ViewModel 에서 컨트롤을 조작하고 있는 경우라면 ViewModel 클래스에 INotifyPropertyChanged 상속을,  Model 에서 컨트롤을 조작하고 있는 경우라면 Model에서 INotifyPropertyChanged 를 상속하고 있어야 한다.)

 

// MainWindow.cs

namespace TestApp {
    public partial class MainWindow:Window {
        InitializeComponent(); // 해당 코드는 생략
        DataContext = new ViewModel(); // DataContext 는 Window 객체에 존재.
    }
    public class ViewModel:INotifyPropertyChanged{
        public ObservableCollection<Model> _Items;
        public ObservableCollection<Model> Items
        {
            get => _Items;
            set
            {
                _Items = value;
                OnPropertyChanged();
            }
        }
        ViewModel() {
            Items = new ObservableCollection<Model>();
            Items.Add(new Model(){ Name = "홍길동"});
        }
        
        // INotifyPropertyChanged 에 정의됨.
        public event PropertyChangedEventHandler PropertyChanged;
        // 편의상 OnPropertyChanged 함수를 정의해줌.
        public void OnPropertyChanged( [CallerMemberName] string propertyName = null)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    public class Model{
        public string Name{ get; set; }
    }
}

 

위와 같이 Items 와 같이 set 안에 OnPropertyChanged 를 호출해주어야

Items 변수가 어떠한 값에 의해서 set 이 됐을 때 Items 변수에 있는 값과 동일하게 View(xaml) 에서도 동기화 처리가 된다.

 

그 외 DependencyObject, ICommand

 

ICommand 는 Button과 같은 Command 속성이 있는 경우에 쓰이고

 

DependencyObject 의 경우에는 DependencyObject.Register 로 등록된 프로퍼티에 대해서 잘못된 값이 들어왔거나 하면 해당 컨트롤에서 잘못됐다는 알림 등을 처리해줄 수 있다.

 

 

'Windows > C#/WPF' 카테고리의 다른 글

[WPF] Main 메소드 호출  (0) 2022.05.11
Controls Styles and Templates  (0) 2022.04.14
[WPF] & 등의 이스케이프 문자 처리  (0) 2021.05.13
[WPF] 나중에 참고할 링크  (0) 2021.05.12
커멘드  (0) 2021.05.12

댓글