[언리얼 MVVM 바인딩 필드 Select 처리] 2. Widget Hierarchy Selection → MVVM Group 자동 동기화

KWONYEONGMIN·2026년 3월 13일

언리얼

목록 보기
13/15
post-thumbnail

고민 : 어떤 키 값을 써야 할지?

⇒ GPT는 선택된 위젯의 이름을 키 값으로 사용하는 방식을 추천했다.

그래서 어떻게 구현해야 할까 ?

초기에는 GPT가 제안한 방식으로 구현을 시도했다. 그러나 Widget Hierarchy는 Engine의 UMG 모듈에, MVVM Binding Panel은 MVVM Plugin에 구현되어 있어 두 시스템을 직접 연결하는 방식은 불필요한 결합도를 만들 수 있다는 것을 확인했다.

엔진 동작을 확인하던 중, Widget Hierarchy에서 위젯을 선택하면 MVVM 패널에서 해당 위젯 이름이 표시되는 흐름이 이미 존재한다는 것을 확인했다.

따라서 별도의 연결 로직을 만드는 대신, Editor 내부에서 이미 제공하고 있는 선택 이벤트 흐름을 활용하는 방식으로 구현 방법을 변경하였다.


Widget Hierarchy 선택 이벤트 분석

해당 MVVM Panel이 구현되어 있는 클래스는 SMVVMViewModelPanel 클래스이다.

void SMVVMViewModelPanel::Construct(const FArguments& InArgs, TSharedPtr<FWidgetBlueprintEditor> WidgetBlueprintEditor)
{
	...

	WidgetBlueprintEditor->OnSelectedWidgetsChanging.AddSP(this, &SMVVMViewModelPanel::HandleEditorSelectionChanged);
 ...
}

SMVVMViewModelPanel 클래스가 생성될 때, FWidgetBlueprintEditorOnSelectedWidgetsChanging 델리게이트에 콜백이 바인딩된다. 즉, Widget Hierarchy에서 위젯 선택이 변경되면 해당 델리게이트가 Broadcast 된다.

⇒ 해당 델리게이트를 사용해서 선택될 때 MVVM GroupRow가 선택 표시되도록 할 것 이다.


MVVM Binding Row 구조 분석

R&D 결과, GroupRow/BindingRow는 SBindingsList::GenerateEntryRow() 에서 생성된다.

TSharedRef<ITableRow> SBindingsList::GenerateEntryRow(TSharedPtr<FBindingEntry> Entry, const TSharedRef<STableViewBase>& OwnerTable)
{
...
		switch (Entry->GetRowType())
		{
			case FBindingEntry::ERowType::Group:
			{
				Row = SNew(UE::MVVM::BindingEntry::SGroupRow, this, ...);
				break;
			}
			case FBindingEntry::ERowType::Binding:
			{
				Row = SNew(UE::MVVM::BindingEntry::SBindingRow, this, ...);
				break;
			}
	...

여기서

  • GroupRow : 위젯 단위의 바인딩 그룹
  • BindingRow : 실제 바인딩 항목

각 위젯마다 하나의 GroupEntry(ERowType::Group) 가 생성된다.


이벤트 처리 위치 결정

처음에는 Row 수준에서 이벤트를 처리하는 것을 고려했다.
하지만 SBindingsList는 MVVM 패널 당 하나의 TreeView로 생성되는 구조이기 때문에, 외부 Selection 이벤트를 Row 단위에서 처리하는 것은 적절하지 않았다.

따라서 MVVM 패널의 최상위 위젯인 SBindingsPanel에서 이벤트를 수신하도록 구조를 설계했다.


선택 이벤트 구현 (Hierarchy → MVVM)

1. 에디터 선택 이벤트 바인딩

void SBindingsPanel::Construct(const FArguments& InArgs, TSharedPtr<FWidgetBlueprintEditor> WidgetBlueprintEditor, bool bInIsDrawerTab)
{
...
	WidgetBlueprintEditor->OnSelectedWidgetsChanged.AddSP(this, &SBindingsPanel::HandleEditorSelectionChanged);
...
}

SBindingsPanel 은 MVVM 패널의 최상위 위젯으로, 외부(UI Hierarchy)에서 발생하는 위젯 선택 이벤트를 수신하기에 가장 적합한 위치다. SBindingsPanel 생성 시 FWidgetBlueprintEditorOnSelectedWidgetsChanged 델리게이트에 콜백을 바인딩한다.

⇒ 이를 통해 Widget Hierarchy에서 선택이 변경될 때마다 MVVM 패널에서 해당 이벤트를 감지할 수 있다.

2. Hierarchy 선택 변경 콜백 처리

해당 콜백함수에서는 BindingsList::SelectGroupByWidgetName 함수를 호출하여 선택함 위젯의 이름을 전달한다.

void SBindingsPanel::HandleEditorSelectionChanged()
{
	TSharedPtr<FWidgetBlueprintEditor> Editor = WeakBlueprintEditor.Pin();
	if (nullptr == Editor.Get()) 
	{
		return;
	}

	TSet<FWidgetReference> Selected = Editor->GetSelectedWidgets();

	if (Selected.Num() == 1)
	{
		const FWidgetReference& Ref = *Selected.CreateConstIterator();
		const FName WidgetName = Ref.GetPreview()->GetFName();

		BindingsList->SelectGroupByWidgetName(WidgetName);
	}
}

여기서는

  1. 현재 선택된 위젯 목록을 가져온다.
  2. 단일 위젯 선택인 경우에만 처리한다.
  3. 선택된 위젯 이름을 SBindingsList에 전달한다.

3. MVVM GroupRow 선택 처리

void SBindingsList::SelectGroupByWidgetName(FName InWidgetName)
{
	if (InWidgetName.IsNone())
	{
		return;
	}

	// 1. 모든 GroupEntry 순회
	for (const TSharedPtr<FBindingEntry>& Entry : AllRootGroups)
	{
		if (false == Entry.IsValid())
		{
			continue;
		}

		if (Entry->GetRowType() != FBindingEntry::ERowType::Group)
		{
			continue;
		}

		// 2. 이 Group이 대표하는 WidgetName 얻기
		FName GroupWidgetName = Entry->GetGroupName();

		// 3. 이름이 같으면 선택 + 스크롤
		if (GroupWidgetName == InWidgetName)
		{
			TreeView->SetSelection(Entry, ESelectInfo::Direct);
			TreeView->RequestScrollIntoView(Entry);
			TreeView->SetItemExpansion(Entry, true);
			return;
		}
	}
}

SBindingsList는 MVVM 바인딩 목록(TreeView)을 직접 관리하는 컴포넌트다. 모든 최상위 GroupEntry(ERowType::Group)를 순회하며 각 Group이 대표하는 위젯 이름과 Hierarchy에서 선택된 위젯 이름을 비교한다.

일치하는 GroupEntry를 찾으면

  • SetSelection을 통해 선택 상태를 갱신하고
  • RequestScrollIntoView로 화면에 노출하며
  • 접혀 있는 경우를 대비해 Group을 확장한다.

이때 ESelectInfo::Direct를 사용하여 선택 동기화로 인한 이벤트 루프를 방지한다.


정리

구조는 다음과 같이 구성된다.

  • SBindingsPanel
    • 에디터의 위젯 선택 변경 이벤트를 구독하는 진입점
  • SBindingsList
    • Widget 이름을 기준으로 GroupRow를 탐색 및 선택하는 역할
  • GroupEntry
    - 위젯 단위로 생성되는 MVVM 바인딩 그룹

결과적으로 다음과 같은 흐름을 구현했다.

Widget Hierarchy 선택

Editor Selection Event

SBindingPanel

SBindingList

MVVM GroupRow 자동 선택 + 스크롤

이를 통해 Widget Hierarchy에서 위젯을 선택하면 MVVM Binding Panel에서도 해당 위젯의 GroupRow가 자동으로 선택되도록 구현할 수 있었다.

profile
Hello World

0개의 댓글