테닝베어의 나날

[C#/WPF] DataBinding 예제 및 설명 본문

카테고리 없음

[C#/WPF] DataBinding 예제 및 설명

테닝베어 2021. 11. 29. 09:00

안녕하세요. 저번 시간에는 버튼을 누르면 카운팅 되어 Label에 숫자가 올라가는 것을 해보았습니다.

저번 시간 내용을 보시려면 아래 링크를 눌러주세요.

2021.11.27 - [분류 전체보기] - [C#/WPF] 버튼 누른 횟수 카운트하기

 

[C#/WPF] 버튼 누른 횟수 카운트하기

저번 글은 Visual Studio 2022 설치 방법 및 프로젝트 생성 방법에 대해서 알아보았습니다. 오늘은 버튼을 누르면 Label에 텍스트가 바뀌는 것을 해보겠습니다. WPF로 본격적으로 무엇을 하기에 앞서,

cw-wd.tistory.com

 

이번 시간에는 DataBinding을 통해서 XAML코드에 직접 접근을 안하고 UI를 바뀌는것을 해보겠습니다.

WPF는 DataBinding을 통한 MVVM패턴을 이용할때 그 가치가 빛을 발합니다.

DataBinding에 대한 자세한 설명은 아래 글을 보시면 됩니다.

2015.07.03 - [IT] - [WPF] 데이터 바인딩

 

[WPF] 데이터 바인딩

1. 데이터 바인딩이란??  '데이터 바인딩'이란, 응용 프로그램의 UI의 요소와 소스코드의 데이터를 서로 연결하는 것이다. '데이터 바인딩'을 사용하고 객체의 값을 변경하면, 바인딩된 컨트롤

cw-wd.tistory.com

 

DataBinding을 다시 간단히 설명하자면, View(XAML)와 소스코드 데이터를 엮는 작업을 뜻합니다.

이렇게 하면 View는 Source를, Source는 View를 몰라도 서로간의 데이터가 변경되었을 때 반영이 가능해 집니다.

여기서 모른다는건 아예 모르는건 아니지만 직접 접근을 안한다라는 뜻입니다.

'이게 뭔 X소리냐..'라고 하실 수 있습니다.ㅎㅎ

예로들어볼게요.

저번 예제의 버튼 클릭했을때의 Source쪽 코드를 보겠습니다.

 

lbTest.Content = "안녕하세요. " + clickCtn++;

 

위 코드에서 보시다시피, Source는 View를 알아야만 lbTest에 접근이 가능해지고, 접근이 가능해져야 Content를 바꿀 수 있습니다.

다행히 이 예제에서는 VIew(MainWindow.xaml)와 Source(MainWindow.cs)는 부분클래스 관계이기 때문에 Source는 lbTest 컨트롤에 접근이 가능해서 사용이 가능했죠. 하지만 이렇게 사용한다면 Source는 View에 의존적인 관계가 되어 버립니다. 다시 말해서 View가 바뀌면(ex) View의 컨트롤 이름이 바뀌면) Source의 변경도 불가피해집니다. 그런게 한 View에 여러개가 있다면? 그만큼 다 바꿔줘야 하는 상황이 일어납니다.

 

DataBinding을 사용하면 위와 같은 고민이 사라집니다. 관계는 의존적인 관계에서 벗어나게 되고, 한 쪽을 바꿔도 다른 한쪽을 안바꿔도 되는 그런 사이가 됩니다. Source는 lbTest를 몰라도 Content를 바꿀 수 있게 되고, View에서는 값이 변경되었을때(TextBox와 같은 입력컨트롤에서..) Source에 실시간 반영이 가능해집니다. 그리고 View와 Source는 별다른 가공 없이 재사용이 가능하게 됩니다.(추후 포스팅을 통해서 설명해 드리겠습니다.)

 

저희는 이러한 기능을 사용하여 MVVM(Model-View-ViewModel) 패턴을 이용한 WPF프로그램을 만들어 볼겁니다.

MVVM 패턴은 차츰 설명해 드리겠습니다.

 

우선 MainWindow.cs 파일의 코드를 다음과 같이 변경해 봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
        private void RaisePropertyChanged(string propName = null)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(propName));
        }
 
 
        private int mClickCnt = 0;
        public int ClickCnt
        {
            get { return mClickCnt; }
            set
            {
                if(mClickCnt != value)
                {
                    mClickCnt = value;
                    RaisePropertyChanged("ClickCnt");
                }
            }
        }
 
        public MainWindow()
        {
            InitializeComponent();
 
            DataContext = this;
        }
 
 
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ClickCnt++;
        }
    }
cs

1 : INotifyPropertyChanged는 WPF에서 DataBinding을 걸기 위한 최소한의 조건입니다. 해당 인터페이스를 구현했다는 것은 PropertyChanged 이벤트가 그 클래스 내부에 있다는 것이고, PropertyChanged가 있다는 것은 우리가 DataBinding을 걸 준비가 되어있다라고 봐도 무방합니다.

 

3 : INotifyPropertyChanged에 있는 이벤트입니다.

 

4~7 : PropertyChanged를 편하게 사용하기 위한 메서드 입니다. 굳이 없어도 무방하지만 만들어두면 편합니다.

 

10~22 : 필드와 속성의 조합은 관용적으로 쓴다 생각하시면 됩니다. 밖으로는 속성만 공개해서 접근가능하게 하고, setter를 통해 값을 변경하면 value 체크를 한 뒤, PropertyChanged 이벤트를 호출하게 하는 구조입니다. RaisePropertyChanged의 매개변수로는 속성의 이름을 넣습니다.

 

28 : DataContext를 나 자신으로 넣습니다. 'View가 어떤 Data를 이용해서 DataBinding을 걸겠다.'라고 정해 주는 것입니다.

 

32~35 : 버튼이 클릭 되었을때 카운트가 증가 되는 로직입니다. 이 이벤트도 의존적인 성격이 있기 때문에 추후에 없애는 것을 보여드리겠습니다.

 

 

이제 Source를 수정하였으니 View쪽 수정하는 것을 보여드리겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<Window x:Class="ExWpf.MainWindow"
        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:ExWpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
 
        <Label
            Grid.Column="0"
            HorizontalContentAlignment="Center"
            VerticalContentAlignment="Center"
            Content="{Binding ClickCnt}"
            />
 
        <Button
            Grid.Column="1"
            Content="눌러주세요."
            Click="Button_Click"
            />
            
    </Grid>
</Window>
 
cs

Label컨트롤을 보면 바뀐게 딱 2가지 입니다.

1. Name 속성이 사라졌다.

2. Content에 {Binding ClickCnt}를 넣는다.

 

Name은 DataBinding을 걸면 굳이 필요 없기에 지웠습니다.

Content에 있는 'Binding'은 DataContext에 있는 데이터를 바인딩 걸겠다(가져다 쓰겠다)고 선언해주는 것이라고 보면 됩니다.

MainWindow.cs 코드에서 DataContext = this했었고, this에는 ClickCnt라는 속성이 있는게 기억 나십니까?

Binding을 사용하면 DataContext에서 'ClickCnt'를 찾아 값을 가져 옵니다.



이제 작동 메커니즘을 정리하자면 다음과 같습니다.

1. Button을 클릭하면 ClickCnt속성이 변경이 됩니다.

2. ClickCnt가 변경되면 PropertyChanged 이벤트를 동작시킵니다(RaisePropertyChanged 속에서 PropertyChanged를 호출함.). 그러면 이를 구독하고 있는 EventHandler들이 호출 됩니다.

3. View에서는 Source가 바뀐걸 인지를 하고 해당 Property값을 가져갑니다.



변경을 다 하셨으면 디버깅 모드 실행을 하시고, 동작하는것을 확인 하시면 됩니다.

아직까지 큰 어려움 없죠?ㅎㅎ 저만 그런가요??ㅎㅎㅎ..

이 예제에서는 Label의 Content로 테스트 해봤으니, Textblock의 Text에도 걸어보시고, button의 content로도 DataBinding을 걸며 연습해 보시기 바랍니다. 큰 어려움은 없습니다.



다음 포스팅에서는 코드를 좀 더 다듬고 Event를 처리 하는 방법을 보여드리겠습니다.

감사합니다.

Comments