[WPF] 3. Binding Mode

soonwoo·2025년 9월 11일

WPF

목록 보기
3/5
post-thumbnail

WPF 데이터 바인딩에서 BindingMode는 Source와 Target간의 데이터 흐름 방향을 결정한다.

WPF에서 제공하는 주요 Bindgind Mode는 4가지가 있는데, 각 Binding Mode와 간략한 설명은 다음과 같다.

  1. OneWay : 데이터가 Source에서 Target으로 흐름
  2. TwoWay : 데이터가 양방향으로 흐름
  3. OneWayToSource : 데이터가 Target에서 Source로 흐름
  4. OneTime : 한 번만 데이터를 Binding

이름이 꽤나 직관적이라 그래도 마음이 편안하다.

그런데, 이전 예제에서 TextBox와 TextBlock, Button등은 따로 Binding Mode를 지정하지 않은 것을 확인 할 수 있다.

이런 경우, 각 컨트롤의 의존 속성마다 미리 정의된 기본 Binding Mode대로 동작한다.

기본 Binding Mode는 사용자가 상호작용으로 값을 편집할 수있는 속성(예를 들어 TextBox.Text나 CheckBox.Checked)의 경우 TwoWay방식으로, 그렇지 않은 경우 OneWay방식으로 동작한다고 보면 된다.

그럼 차례대로 각 Binding Mode에 대해 자세히 알아보도록 하자!

(예시들은 이전 포스팅의 코드를 이용했습니다. 해당 예제 코드를 띄워놓고 따라오시는걸 권장합니다.)

1. OneWay

Source \rarr Target 단방향 데이터 흐름

ViewModel 속성 (Source) ──→ UI 컨트롤 속성 (Target)
        ↑                           ↓
   PropertyChanged              UI 업데이트
<TextBlock Text="{Binding CharacterCount}"/>

Default Binding Mode가 OneWay인 TextBlock
ViewModel이 UI를 업데이트 하는 흐름이다.

  • InputString이 변경되면 CharacterCount속성도 변경됨
  • OnPropertyChanged(nameof(CharacterCount))호출
  • TextBlock의 Text가 자동으로 업데이트 됨

\rarr UI에서 ViewModel쪽으로는 업데이트 안됨!

2. TwoWay

Source \larr\rarr Target 양방향 데이터 흐름

ViewModel 속성 (Source) ←──→ UI 컨트롤 속성 (Target)
        ↑                           ↓
   PropertyChanged              UI 업데이트
        ↓                           ↑
  속성값 변경                  사용자 입력
<TextBox Text="{Binding InputString, UpdateSourceTrigger=PropertyChanged}"/>
  • 사용자가 타이핑 \rarr TextBox.Text변경
  • UpdateSourceTrigger=PropertyChanged로 인해 즉시 ViewModel.InputString 업데이트
  • InputString setter 실행
  • CharacterCount 변경 \rarr TextBlock 자동 업데이트

여기서 궁금했던게, 어차피 xaml에서 UpdateSourceTrigger=PropertyChanged 이걸로 타이핑 할 때마다 InputString이 업데이트 되는데,
INotifyPropertyChanged를 상속받아서

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
}

왜 굳이 이 부분으로 CharacterCount를 UI에 업데이트 해주나?
어차피 ViewModel은 UpdateSourceTrigger=PropertyChanged을 통해 타이핑 할 때마다 InputString의 길이가 변한다는 사실은 알텐데..

근데 이게 MVVM의 특징인 것 같다.

ViewModel에서는 UI를 직접 조작하지 않고, 오직 데이터 바인딩을 통해서만 소통해야하기 때문에

UpdateSourceTrigger=PropertyChanged를 통해 UI에서 ViewModel에 업데이트 정보를 전달하고,

INotifyPropertyChanged가 이제 ViewModel에서 UI로 표출하도록 역할이 명확히 나눠져 있는 것이다.


3. OneWayToSource

Target \rarr Source 단방향 데이터 흐름.
OneWay와 정반대 방향.

ViewModel 속성 (Source) ←── UI 컨트롤 속성 (Target)
        ↑                           
   속성값 업데이트               사용자 조작/변경
        ↑                           ↑
   PropertyChanged 발생 안함    UI 이벤트 발생

바로 비교해보자. OneWay vs OneWayToSource

<!-- OneWay: ViewModel → UI만 -->
<TextBlock Text="{Binding Status, Mode=OneWay}" />
<!-- ViewModel의 Status 변경 → TextBlock 업데이트 -->
<!-- TextBlock 변경(불가능) → ViewModel 영향 없음 -->

<!-- OneWayToSource: UI → ViewModel만 -->
<Slider Value="{Binding Volume, Mode=OneWayToSource}" />
<!-- ViewModel의 Volume 변경 → Slider 업데이트 안됨 -->
<!-- Slider 조작 → ViewModel의 Volume 업데이트됨 -->

그런데 대부분의 경우 TwoWay가 더 적합하다고 한다.
왜냐하면 사용자 입력 필드는 보통 TwoWay가 필요하고,
OneWayToSource는 특수한 상황에서만 사용하기 때문이다.

여기서 사용자 입력 필드는 OneWayToSource로 해도 TwoWay와 비교하면 기능상 아무 차이 없는게 아닌가!

라는 생각이 들 수도 있지만, 나도 그런 의문을 가졌던 사람으로서 아주 속시원한 반박 예제를 가지고 왔다.

  1. 초기값 세팅 문제
public MainViewModel()
{
    InputString = "기본값"; // OneWayToSource에서는 TextBox에 표시 안됨!
}

TwoWay였다면 TextBox에 "기본값" 이라는게 표시 될 것이다.
하지만 OneWayToSource에서는 TextBox가 비어있다!

  1. Undo/Redo 기능
private Stack<string> _undoStack = new Stack<string>();

public void Undo()
{
    if (_undoStack.Count > 0)
    {
        InputString = _undoStack.Pop(); // OneWayToSource에서는 UI에 반영 안됨!
    }
}

그래서 사용자 입력 필드는 대부분 TwoWay를 사용하는 것이 안전하고 예측 가능하다.
OneWayToSource는 정말 특별한 경우(UI 상태 추적, 사용자 액션 로깅 등)에만 사용하는 것이 좋당


3. OneTime

바인딩 생성 시점에 단 한 번만 데이터를 복사하는 바인딩 모드.

바인딩 생성 시점:
ViewModel 속성 (Source) ──→ UI 컨트롤 속성 (Target)
                       한 번만!

이후:
ViewModel 속성 변경 ──X──→ UI 컨트롤 (업데이트 안됨)
UI 컨트롤 변경 ──X──→ ViewModel 속성 (업데이트 안됨)

대표적으로 애플리케이션 시작 시 설정되고 변경되지 않는 값들에 사용하면 아주 좋다.

<!-- 앱 제목 - 런타임 중 변경되지 않음 -->
<Window Title="{Binding ApplicationTitle, Mode=OneTime}">

<!-- 앱 버전 정보 -->
<TextBlock Text="{Binding Version, Mode=OneTime}" />

<!-- 회사 로고 경로 -->
<Image Source="{Binding CompanyLogo, Mode=OneTime}" />



출처 : https://cs-solution.tistory.com/38

0개의 댓글