WPF에서 MVVM 패턴이 어떻게 사용되는지에 대한 예제를 안내해 드리려고 합니다. 시작하도록 하겠습니다. 😘
MVVM(model-view-viewmodel) 패턴 예제
성명 입력, 생년월일 입력 예제입니다.
성(last name) 또는 명(first name)을 입력하는 순간 바인딩 된 성명(full name)을 표시합니다.
또한 생년월일이 숫자로 입력이 되었는지 점검해보겠습니다.
MVVM 예제의 결과 화면입니다. 그럼 시작해봅시다~
INotifyPropertyChanged (System.ComponentModel)
INotifyPropertyChanged 인터페이스를 상속하겠습니다. (Property value 가 Changed 될 때 Notify 하는 인터페이스)
class NotifierBeomSang : INotifyPropertyChanged 와 같이 클래스를 생성하겠습니다. 그리고 이것을 모델로 사용할 예정이랍니다.
매개변수는 2가지를 준비해 보았습니다. 하나는 string으로 해볼 것이며, 다른 하나는 PropertyChangedEventArgs로 해보겠습니다. 자신에게 맞는 방법을 사용하시면 되며, 다른 더 좋은 방법이 있다면 그리 진행하셔도 된답니다. NotifyChanged 메소드를 이용하여 뷰-모델을 연결하겠습니다.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BeomMVVM.Model
{
class NotifierBeomSang : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void NotifyChanged(params string[] propertyNames)
{
foreach (string name in propertyNames)
{
//OnPropertyChanged(name);
OnPropertyChanged(new PropertyChangedEventArgs(name));
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
MVVM ViewModel : Notifier
뷰모델입니다. 아까 만든 모델 NotifierBeomSang 클래스를 상속하였습니다. (class ViewModelBeomSang : NotifierBeomSang) 상속한 클래스는 당연히 NotifyChanged 메소드를 호출할 수 있을 것입니다.
- Last Name 설정 : Full Name 또한 수정
- First Name 설정 : Full Name 또한 수정
- Birth 설정 : 숫자여부 점검, birthError 값 수정, birthErrorMsg 값 수정
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace BeomMVVM.Model
{
class ViewModelBeomSang : NotifierBeomSang
{
string lastName = string.Empty;//성
string firstName = string.Empty;//명
string fullName = string.Empty;//성명
string birth = string.Empty;
bool isBirthError = false;
public string LastName
{
get { return lastName; }
set
{
lastName = value;
fullName = lastName + firstName;
//이름 및 성명전체를 수정합니다.
NotifyChanged("LastName", "FullName");
//NotifyChanged("LastName");
//NotifyChanged("FullName");
}
}
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
fullName = lastName + firstName;
//성 및 성명전체를 수정합니다.
NotifyChanged("FirstName", "FullName");
}
}
public string FullName
{
get { return fullName; }
}
public string Birth
{
get { return birth; }
set
{
if (Regex.IsMatch(value, "^[0-9]*$"))
{
birth = value;
isBirthError = false;
}
else
{
//birth = value;
birth = value;
isBirthError = true;
}
NotifyChanged("Birth");
NotifyChanged("BirthErr");
NotifyChanged("BirthErrMsg");
}
}
public bool BirthErr
{
get
{
return isBirthError;
}
}
public string BirthErrMsg
{
get
{
if (!isBirthError)
return $"입력하신 생년월일 : {birth}";
else
return $"생년월일은 숫자로 입력해 주세요 : {birth}";
}
}
}
}
MVVM View.xaml.cs
MVVM 뷰 부분입니다. 앞서 만든 ViewModel을 DataContext로 설정하겠습니다. 뷰모델을 연결하는 부분에 주목해 주십시오.
public partial class ViewBeomSang : Window
{
public ViewBeomSang()
{
InitializeComponent();
this.DataContext = new ViewModelBeomSang();
}
}
MVVM View.xaml
레이블과 텍스트박스를 간단하게 추가해보았습니다. Text 와 Content 는 뷰모델과 바인딩(binding)을 진행합니다. UpdateSourceTrigger=PropertyChanged 이 부분을 추가한 이유는, 텍스트박스에 대한 Default 가 LostFocus 이기 때문에 즉각적인 수정을 위하여 추가하였습니다. 데이터트리거는 생년월일을 숫자로 입력하지 않았을 때, 붉게 표시해드리는 예제로 추가했습니다.
<Window x:Class="BeomMVVM.View.ViewBeomSang"
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:BeomMVVM.View"
mc:Ignorable="d"
Title="ViewBeomSang" Height="450" Width="800">
<Grid>
<Label Content="성(last name)" HorizontalAlignment="Left" Margin="54,48,0,0" VerticalAlignment="Top" Width="105"/>
<TextBox x:Name="txtLastName" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="179,51,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label Content="명(first name)" HorizontalAlignment="Left" Margin="54,85,0,0" VerticalAlignment="Top" Width="105"/>
<TextBox x:Name="txtFirstName" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="179,88,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label Content="Full Name : " HorizontalAlignment="Left" Margin="331,48,0,0" VerticalAlignment="Top"/>
<Label Content="{Binding FullName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="413,48,0,0" VerticalAlignment="Top" Height="26" Width="224"/>
<Label Content="생년월일(숫자)" HorizontalAlignment="Left" Margin="54,127,0,0" VerticalAlignment="Top" Width="105"/>
<TextBox x:Name="txtBirth" Text="{Binding Birth, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="179,130,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label Content="{Binding BirthErrMsg, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="369,130,0,0" VerticalAlignment="Top" Height="103" Width="413">
<Label.Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Black" />
<Style.Triggers>
<DataTrigger Binding="{Binding BirthErr}" Value="TRUE">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</Grid>
</Window>