How-To: Build an MVVM Screen

Goal: a screen whose visuals bind to a ViewModel through UE's ModelViewViewModel (MVVM) plugin, using the Crimson bases so every ViewModel can reach its ULocalPlayer.

Prerequisites
The engine's ModelViewViewModel plugin is enabled (it comes with CrimsonUI). Basic familiarity with the UMG Viewmodel panel helps - the MVVM specifics below are standard engine workflow.

1. Pick the right base

BaseUse forAdds
UCrimsonViewModelBaseAny ViewModelInitialize(LocalPlayer) (BlueprintCallable), GetLocalPlayer() (BlueprintPure), NativeOnInitialized (BlueprintNativeEvent) - a hook that fires once the local player is set
UCrimsonScreenViewModelBaseOne ViewModel per screen/pageNothing - semantic marker so screen VMs are distinguishable from entry VMs
UCrimsonEntryViewModelBaseOne ViewModel per list/tile rowEntryIndex (FieldNotify) so row widgets can bind to their own position

2. Author the ViewModel

Screenshot pendingImages/CrimsonUI/howto-mvvm-viewmodel-bp.png
BP_InventoryVM (parent: Crimson Screen View Model Base): add FieldNotify variables (e.g. GoldText), override Native On Initialized -> Get Local Player -> pull initial state.

Create a Blueprint Class with parent Crimson Screen View Model Base. Add variables and tick Field Notify on each one the view should react to. Override the Native On Initialized event to pull initial state - Get Local Player is valid from that point on.

3. Bind the view and initialize

In the screen widget open Window -> Viewmodels, add your ViewModel class (creation mode 'Create Instance' is simplest), and add View Bindings from its FieldNotify variables to widget properties. Then initialize the VM when the screen activates:

Screenshot pendingImages/CrimsonUI/howto-mvvm-bindings.png
Viewmodels panel: BP_InventoryVM added; View Bindings map GoldText -> TextBlock.Text. Graph: Event On Activated -> Get Viewmodel -> Initialize (Local Player = Get Owning Local Player).
Bind on activation, not on construct
Activatable widgets are pooled and reused by their layer stack. Initialize / refresh the ViewModel in On Activated, not On Initialized/Construct - otherwise a reopened screen shows the data from the last time it was open.
Verify
Changing a FieldNotify variable (e.g. via a test button calling its setter) updates the bound widget instantly; reopening the screen shows fresh data.

4. Entry ViewModels for list rows

Give each row a UCrimsonEntryViewModelBase subclass. The owning code sets EntryIndex (C++ SetEntryIndex); the row widget binds to it (FieldNotify) for striping, numbering, or selection logic. This pairs with How-To: Lists with Mixed Item Types - the entry widget grabs its VM in OnListItemObjectSet.

See also

  • How-To: Lists with Mixed Item Types - the view side of entry VMs.
  • How-To: Push & Pop Screens - activation lifecycle the VM init rides on.