본문 바로가기
Study/WPF

[WPF][MVVM][Study] MVVM 실습 1 - 단위 변환기

by 스테디코디스트 2023. 9. 20.
반응형
MVVM을 만들면서 이해하기 위해 간단한 실습을 해보았다.
아래 블로그를 참고해서 만들었다.
https://esound.tistory.com/10
 

[C# WPF] MVVM 간단하게 시작하기 - 1 (데이터바인딩, 연동)

MVVM, Model - View - ViewModel WPF에서 사용할 수 있는 디자인패턴입니다. 이번 글과 앞으로 이어지는 글에서 MVVM을 쉽고 간단하게 사용할 수 있도록 공부하며 배운 내용을 정리하겠습니다. View : 사용자

esound.tistory.com

 

그럼 지금부터 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. 결과