MVVM을 만들면서 이해하기 위해 간단한 실습을 해보았다.
아래 블로그를 참고해서 만들었다.
https://esound.tistory.com/10
그럼 지금부터 WPF에서 MVVM 패턴을 이용한 단위 변환기 만들기 설명을 시작하겠다.
1. WPF 시작
- 기본적으로 WPF 앱 프로젝트 파일을 생성한다.
2. 폴더 작업
- 솔루션 탐색기에서 프로젝트 하위에 Model, ViewModel 폴더를 만들어주고, 각 폴더에는 클래스 파일을 만들어 준다.
- 프로젝트>추가>새 폴더
- 파일>추가>클래스
3. 뷰를 생성한다.
- 기본 XAML 파일이 있는 윈도우로 들어가 도구상자를 이용해 간단하게 뷰를 만들어준다.
- 텍스트를 나타낼 Label 3개를 만들었고, 입력을 받을 공간을 TextBox로 2개 만들어 주었다.
4. 코드 작성
1) 실시간 UI 변경
- 실시간으로 UI를 반영해주기 위해 먼저 System.ComponentModel을 using 해주어 INotifyPropertyChanged 인터페이스 각 클래스에 상속시켜준다.
- 인터페이스 내부 함수 구현을 위해 alt+Enter를 이용하여 쉽게 구현해주면 PropertyChanged라는 이벤트가 자동으로 생성된다.
- 이후 이벤트 발생시 실행할 함수를 구현해준다.(Model, ViewModel 동일하게 구현)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace MVVM_EX_1.Model
{
class MainModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace MVVM_EX_1.ViewModel
{
class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
2) Model - 나머지 코드
- 모델에서는 내부의 일인 byte를 bit로 바꿔주는 일이 일어나야 한다.
- 외부에서는 실제 값에는 직접 접근하지 못하도록 get,set property를 이용하여 코드를 작성한다.
- set 함수 작성시 마지막에 직전에 구한 이벤트함수 OnPropertyChanged를 이용해 해당 부분에 숫자를 입력하면 실시간으로 UI가 반영하도록 해준다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace MVVM_EX_1.Model
{
class MainModel : INotifyPropertyChanged
{
private int inputByte = 0; // 입력할 바이트
public int InputByte // InputBit로 접근
{
get => inputByte;
set
{
inputByte = value;
OutputBit = value * 8; // 1byte = 8bit
OnPropertyChanged("InputByte"); // UI 실시간 변경
}
}
private int outputBit = 0; // 출력할 비트
public int OutputBit
{
get => outputBit;
set
{
outputBit = value;
OnPropertyChanged("OutputBit"); // UI 실시간 변경
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
3) ViewModel - 나머지 코드
- 뷰 모델은 모델과 뷰를 이어주는 역할을 해야하므로 모델을 생성하고, 모델에 접근하여 동작하도록 한다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace MVVM_EX_1.ViewModel
{
class MainViewModel : INotifyPropertyChanged
{
private Model.MainModel model = null;
public MainViewModel() // 생성자
{
// 모델을 생성해줌
model = new Model.MainModel();
}
public Model.MainModel Model // model에 접근
{
get => model;
set
{
model = value;
OnPropertyChanged("Model"); // UI 실시간 변경
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
5. 뷰와 뷰 모델을 연결하기
- MVVM 패턴 특성상 뷰는 뷰 모델에 연결되고, 뷰 모델에서 모델과 연결되어 동작을 하는 것이기에, 뷰에는 뷰 모델만 연결 시켜주면 된다.
- DataContext를 뷰 모델로 설정하여 연결할 것이다.
- 뷰의 윈도우 창 클릭>속성 창> 공용>DataContext>새로 만들기>MainViewModel(뷰모델이름) 선택 후 확인
(만약 새로 만들기에서 MainViewModel이 안 뜬다면 빌드 후 다시 시도 해보기!)
6. 바인딩 코드 작성
- 뷰와 뷰 모델의 연결이 끝났다면 실제로 바인딩을 시켜 해당 Data가 연동되도록 해야한다.
- 뷰의 xaml에서 bit의 숫자와 byte의 숫자를 나타낼 TextBox의 Text를 뷰 모델을 통해 모델에 바인딩 해준다.
- byte의 위치에는 모델의 InputByte와 연결해야 하므로 Model.InputByte에 바인딩하는데, 이때 Model은 뷰 모델의 Model이므로 해당 생성자를 통해 실제 모델(MainModel)을 생성하고, MainModel에 있는 InputByte와 연결되게 된다.
- 또한 UpdateSourceTrigger를 PropertyChanged로 설정해 실시간으로 UI가 변동되는 속성을 부여해준다.
- bit도 같은 방식으로 OutputByte에 바인딩해 주면 된다.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MVVM_EX_1"
xmlns:ViewModel="clr-namespace:MVVM_EX_1.ViewModel" x:Class="MVVM_EX_1.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<ViewModel:MainViewModel/>
</Window.DataContext>
<Grid>
<Label Content="단위변환" Margin="215,54,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="64"/>
<Label Content="byte" HorizontalAlignment="Left" Margin="120,85,0,0" VerticalAlignment="Top" Height="28"/>
<Label Content="bit" HorizontalAlignment="Left" Margin="345,85,0,0" VerticalAlignment="Top" Height="28" Width="35"/>
<!--바인딩하는 부분-->
<TextBox HorizontalAlignment="Left" Height="23" Margin="80,118,0,0" TextWrapping="Wrap" Text="{Binding Model.InputByte, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="305,118,0,0" TextWrapping="Wrap" Text="{Binding Model.OutputBit, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
<!--바인딩하는 부분-->
</Grid>
</Window>
7. 결과