WPF 바인딩 질문입니다. (유저컨트롤, Binding, 트리구조)

  • 무엇을 하고자 하는지

메인 윈도우에서 TextBox(또는 UserControl)에 위의 모델의 값중 마지막 TestSet의 TestName을 표시하려 합니다.

문제는 제가 이 글에 업로드한 방식과 동일하게
제가 만든 프로그램에 적용하려 하니 표시가 되지 않는다는 것입니다.
또는 처음 프로그램 로딩시 초기 값이 표시된 뒤로는 업데이트가 되지 않습니다.

적용하려는 프로그램의 창이 많고 레이아웃이 복잡하단 차이는 있지만 특징적인 차이는 없습니다.

UserControl로 컨트롤을 만들어서 작성하고 있는데
DependencyProperty로 연결해서 사용하여도 업데이트 문제는 같습니다.

업데이트가 잘 되는 경우는
위의 모델의 가운데 값 TestCode는 동작을 잘 합니다.

  • 현재 작성한 코드 중 문제가 되는 부분

** 안되는 코드
TextBox Text=“{Binding TestSummaryData[0].TestSets[0].TestName, Mode=TwoWay, updateSourceTrigger=PropertyChanged}”/

** 되는 코드
TextBox Text=“{Binding TestSummaryData[0].TestCode, Mode=TwoWay, updateSourceTrigger=PropertyChanged}”/

  • 기대하는 동작
  1. 모델의 깊이?에 따라 바인딩이 되고 안되고가 결정되는 요인이 있는지 궁금합니다.
  2. 왜 테스트용으로 새 프로젝트에 동일 구조로 만든 코드는 잘 되는지 궁금합니다.
  3. 저런 단계적 모델을 설계할때 어떻게 사용하시는지 궁금합니다.

혹시 필요할지 몰라 새 프로젝트에 테스트해본 프로젝트파일 올립니다.
(변수명등은 조금 차이가 있을 수 있습니다.)

프로젝트 다운로드

TL;DR
List<T>대신 ObservableCollection<T>을 사용해보세요.


추측컨데 Binding Path에 오타가 있는것이 아니라면, MainWindow의 생성자 내에서 DataContext를 지정한 이후로 TestDataModel.TestSummartData 또는 TestSummary.TestSets 리스트에 항목을 추가하셨거나 리스트의 특정 인덱스에 직접 할당하셨을 것 같습니다.

public MainWindow()
{
    InitializeComponent();

    DMinit();

    DataContext = DM;

    .... // 이 부분
  
    Loaded += MainWindow_Loaded; // 혹은 이 부분
}

예상하는 코드는 업데이트 코드는 아래와 같을 것입니다.

VM.TestSummaryData.Add(new TestSummary { ... });
// 또는
VM.TestSummaryData[0] = new TestSummary { ... };
// 또는
VM.TestSummaryData[0].TestSets.Add(new TestSet { ... });
// 또는
VM.TestSummaryData[0].TestSets[0] = new TestSet { ... };

데이터 바인딩의 핵심은 Binding Path에 포함된 요소들이 변경되었다는 것을 바인딩 엔진에게 알려주는 것입니다. 이를 구현하는 장치가 크게 DependencyProperty, INotifyPropertyChanged 입니다.

<TextBox Text="{Binding TestSummaryData[0].TestSets[0].TestName, Mode=TwoWay, updateSourceTrigger=PropertyChanged}"/>

위 바인딩 구문에서 Binding Path에 포함(변경 시 바인딩 타겟을 업데이트 시키는)된 요소는

  1. (DataContext): 상위 Loginal Tree로부터 내려온 DataContext 인스턴스
  2. TestSummaryData: DataContextTestSummaryData 속성
  3. TestSummaryData[0]: 2번 리스트내 0번째 요소
  4. TestSets: 3번 인스턴스의 TestSets 속성
  5. TestSets[0]: 4번 리스트 내 0번째 요소
  6. TestName: 5번 인스턴스의 TestName 속성

올려주신 그림에서 PropertyChanged 이벤트를 발생시키도록 작성하신 속성은 TestSummary 클래스의 TestCode 속성, TestSet 클래스의 TestName, Duration, UpperLimit 속성들 이렇게 네 부분으로 추측됩니다. 따라서 위 코드에서 지정하신 Binding Path내에 포함된 TestName이 변경될 경우 바인딩 업데이트가 발생할 것입니다.

그런데 2번과 4번에 해당하는 TestSummaryData[0]TestSets[0]의 값이 변경됐을 때 이를 바인딩 엔진에게 알려줄 추가적인 장치가 필요합니다.

이것이 바로 INotifyCollectionChanged이며 이를 구현해 놓은 자료구조가 ObservableCollection<T>입니다.
List는 의 길이가 0인 상태에서 새로운 요소가 추가되거나 이미 인스턴스가 할당된 인덱스에 새로운 인스턴스를 지정할 때 이를 알려줄 방법을 제공하지 않습니다.

따라서 List<T> 형식으로 선언된 TestDataModel.TestSummaryData 속성과 TestSummary.TestSets 속성의 형식을 ObservableCollection<T>로 바꿔서 테스트 해보시기 바랍니다.

위 개념만 숙지하신다면 모델 깊이에 따른 바인딩 결정 요인이나 단계적 모델 설계에 대한 특별한 고려 사항은 없을 듯 합니다.

2개의 좋아요

잘되는 프로젝트 말고 재현되는 안되는 프로젝트를 첨부해주세요.

ObservableCollection라는 키워드를 알려주셔서
안되는 프로젝트에 적용하였더니 잘 되는것을 확인하였습니다.

어설픈 질문이었는데 좋은 답변 감사합니다.

제가 올리고 싶지만 이어받은 프로젝트라 내용이 방대하고 꼬여있어서 올릴 수 없었어요. ㅠㅠ