[WPF] MVVM 패턴 예제 (WPF MVVM Pattern Example)

WPF에서 MVVM 패턴이 어떻게 사용되는지에 대한 예제를 안내해 드리려고 합니다. 시작하도록 하겠습니다. 😘

MVVM(model-view-viewmodel) 패턴 예제

mvvm view

성명 입력, 생년월일 입력 예제입니다.
성(last name) 또는 명(first name)을 입력하는 순간 바인딩 된 성명(full name)을 표시합니다.
또한 생년월일이 숫자로 입력이 되었는지 점검해보겠습니다.

mvvm view result

MVVM 예제의 결과 화면입니다. 그럼 시작해봅시다~

INotifyPropertyChanged (System.ComponentModel)

INotifyPropertyChanged

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

Notifier inheritance

뷰모델입니다. 아까 만든 모델 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 view.xaml.cs

MVVM 뷰 부분입니다. 앞서 만든 ViewModel을 DataContext로 설정하겠습니다. 뷰모델을 연결하는 부분에 주목해 주십시오.

public partial class ViewBeomSang : Window
    {
        public ViewBeomSang()
        {
            InitializeComponent();

            this.DataContext = new ViewModelBeomSang();
        }
    }

MVVM View.xaml

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>

댓글