JHB의 프로그래밍 삽질기

[WPF] cs 파일에 있는 변수를 xaml에서 binding 하기 본문

PROGRAMMING/C# WPF

[WPF] cs 파일에 있는 변수를 xaml에서 binding 하기

roter 2018.04.22 02:30

WPF를 처음 접하면서 제일 어려운 개념이 바로 '바인딩'이다.

이제 개념을 차근차근 알아가는 중인데... 여러가지 실험을 해봤고 그 결과를 여기에 올린다.


우선 MainWindow.cs 파일에 아래와 같이 ObservableCollection 두개(멤버변수와 프로퍼티)를 두었다.

public ObservableCollection<string> collections;
public ObservableCollection<string> Collections { get { return collections; } set { collections = value; } }


그리고 item을 아래와 같이 추가했다.

public MainWindow()
{
InitializeComponent();
collections = new ObservableCollection<string>();
collections.Add("APPLE");
collections.Add("BANANA");
collections.Add("COLA");
}

그리고 이제 MainWindow.xaml 파일에 ListBox를 아래와 같이 하나 만들었다


<ListBox x:Name="LbTmp" ItemsSource="{Binding Collections}" />


이게 될까? 안될까?

답은, 안된다.


되게 하려면 어떻게 해야할까?

한줄만 추가하면 된다.

public MainWindow()
{
InitializeComponent();
DataContext = this;

collections = new ObservableCollection<string>();
collections.Add("APPLE");
collections.Add("BANANA");
collections.Add("COLA");
}

위처럼 DataContext = this를 해주면 바인딩이 성공한다.


이제 몇가지 흥미로운 실험을 해봤고 그 결과는 아래와 같다.

1. xaml에서 property인 Collections가 아닌 멤버 변수 collections를 바인딩 하려고 시도하면 실패한다. 아마 무조건 Property형태만 xaml에서 get으로 가져올 수 있도록 돼있는 것 같다.


2. xaml에서 DataContext를 지정하는 방법

Window 태그 내부에

DataContext="{Binding RelativeSource={RelativeSource Self}}"

를 넣으면 DataContext로 MainWindow 본인 스스로의 객체가 설정되는 것 같다.

문제는 이렇게 xaml에서 DataContext를 바인딩 한 경우

public MainWindow()
{
collections = new ObservableCollection<string>();
collections.Add("APPLE");
collections.Add("BANANA");
collections.Add("COLA");
InitializeComponent(); //
}

위처럼 InitializeComponent()의 위쪽에 Property 설정을 끝내줘야 한다.

이유가 뭘까 -_-


3. DataContext 를 지정 안하고도 할 수 있다.

Window 태그에 Name="Window1" 이런식으로 이름을 정하고 나서

<ListBox x:Name="LbTmp" ItemsSource="{Binding ElementName=Window1, Path=Collections}" />

위와 같이 ListBox에서 ItemsSource를 바인딩 할 때, ElementName=Window1 로 지정하고 Path를 Collections로 해줬더니 또 잘 된다.

2번과 마찬가지로, InitializeComponent()의 위쪽에서 프로퍼티 설정을 완료해줘야 한다.

도대체 왜일까 -_-


결론적으로 가장 안전한 방법은 그냥 cs 소스파일에서 DataContext = this 해주고..

xaml에서 Binding Collections 이렇게 해주는게 제일 나은 것 같다.


도대체 InitializeComponent가 뭐하는 놈이길래...

찾아보니

public void InitializeComponent()
{
#line 4 "..\..\App.xaml"
this.StartupUri = new System.Uri("MainWindow.xaml", System.UriKind.Relative);
#line default
#line hidden
}

어플리케이션 실행 시 위 코드 실행 후 그냥 Run 해서 Application이 실행되는데

유추해보자면 xaml 읽어와서 연결하는 놈인 것이다.


근데 어째서 왜... DataContext를 xaml에서 연결하거나 3번 방식으로 바인딩을 시도 할 때

InitializeComponent()보다 아래에 쓰면 바인딩이 안되는 것일까?


그 답은 천천히 돌려보니 알겠더라.


어짜피 바인딩 하는 놈은 '인스턴스'일 텐데,,, 라고 생각해보니 아귀가 딱 맞는다.


public MainWindow()
{
InitializeComponent();
collections = new ObservableCollection<string>();
collections.Add("APPLE");
collections.Add("BANANA");
collections.Add("COLA");
}

만약 위와 같이 해놨다면

InitializeComponent()를 통해 xaml 구문을 읽고 해석을 했을 텐데,

해석 과정에서 

ItemsSource="{Binding Collections}"

위 구문을 수행했을 것이고, Collections은 현재 인스턴스가 없는 상태, 즉 null을 갖고 있기 때문에 바인딩에 실패한다!

그래서 아래와 같이 멤버 변수 선언과 함께 인스턴스를 생성해줬더니 바인딩에 성공했다.

public ObservableCollection<string> collections = new ObservableCollection<string>();


그럼 아래는 어떻게 다를까?

1. DataContext = this를 InitializeComponent() 위에 위치 시키기

public MainWindow()
{
DataContext = this;

InitializeComponent(); //
collections = new ObservableCollection<string>();
collections.Add("APPLE");
collections.Add("BANANA");
collections.Add("COLA");
}


2. DataContext = this를 InitializeComponent() 바로 아래 위치 시키기

public MainWindow()
{
InitializeComponent(); //
DataContext = this;

collections = new ObservableCollection<string>();
collections.Add("APPLE");
collections.Add("BANANA");
collections.Add("COLA");
}


3. DataContext = this를 생성자의 제일 끝에 위치시키기

public MainWindow()
{
InitializeComponent(); //

collections = new ObservableCollection<string>();
collections.Add("APPLE");
collections.Add("BANANA");
collections.Add("COLA");

DataContext = this;
}


결과를 말해보자면

1. DataContext = this를 InitializeComponent() 위에 위치 시키기

은 동작 안한다. 추측해보건데, InitializeComponent()를 거치면서 뭔가 차일드에 설정된 바인딩 들을 다시 한번 실행하나보다.

그 순간에 Collections는 null instance이기 때문에 바인딩에 실패! 라고 추측된다.


[2. DataContext = this를 InitializeComponent() 바로 아래 위치 시키기] 와 [3. DataContext = this를 생성자의 제일 끝에 위치시키기]

은 둘다 잘 된다.

3이야 Collections의 instance가 생긴 뒤니깐.. 이해가 되지만 2는 어째서 가능한 것일까..

아마도 DataContext = this 라는 코드를 통해 컨텍스트를 지정하는 행위는

인스턴스가 아닌 DataContext안에 존재하는 Property 자체를 결합하는 것. 이라는 추측을 해볼 뿐이다. (나도 허접이라 잘 모름)


여기까지 몇가지 실험을 해봤다.

흥미로운 WPF.. 그리고 바인딩...


열심히 공부하자 ~_~





0 Comments
댓글쓰기 폼