View Issue Details

IDProjectCategoryView StatusLast Update
0005278NoesisGUIUnrealpublic2026-10-26 11:18
Reporternjahs Assigned Tohcpizzi  
PrioritynormalSeverityminor 
Status assignedResolutionopen 
Product Version4.0 
Target Version4.0.2 
Summary0005278: Field with plain native getter does not update properly
Description

With the UE MVVM pattern UPROPERTY(FieldNotify, Getter) T Foo; plus a plain native getter UFUNCTION() T GetFoo() const, a Noesis binding to Foo reads the correct value initially but never updates when the property is changed and its FieldNotify field is broadcast (for example with UE_MVVM_SET_PROPERTY_VALUE). The same happens for UPROPERTY(FieldNotify, Getter="GetFoo") T bFoo;. The same ViewModel works if the getter itself is a FieldNotify function or if there is no getter at all.

Cause: the getter pass of NoesisFillTypeClassForUClass creates the binding Foo and registers a field notification only for the getter function's field id, which is invalid for a plain function. The property pass then skips the property because a binding with that name already exists, so the property's field id is never mapped.

Background:
UE's MVVM framework (UMVVMViewModelBase, FieldNotification module) recommends a reflected, FieldNotify property with an optional getter:

FString Foo;                       // "Getter" without value: GetFoo
UFUNCTION() FString GetFoo() const { return Foo; }   // native getter, not Blueprint-exposed
void SetFooValue(const FString& NewValue) { UE_MVVM_SET_PROPERTY_VALUE(Foo, NewValue); }  // broadcasts the field id of Foo

Projects use this pattern because the property is the single source of truth for Blueprint and for MVVM field notifications, and the getter gives C++ and Noesis a stable name (GetFoo becomes the Noesis binding Foo, bFoo with Getter="GetFoo" is the usual boolean variant). UE_MVVM_SET_PROPERTY_VALUE broadcasts the field id of the property. It has no way to know about a plain getter function.

Repro:

#include "MVVMViewModelBase.h"
#include "ExampleViewModel.generated.h"

UCLASS()
class UExampleViewModel : public UMVVMViewModelBase
{
    GENERATED_BODY()
public:
    UPROPERTY(BlueprintReadOnly, FieldNotify, Getter, Category="Example")
    FString Foo;

    UFUNCTION() FString GetFoo() const { return Foo; }          // plain getter, NOT FieldNotify

    void Change(const FString& NewValue) { UE_MVVM_SET_PROPERTY_VALUE(Foo, NewValue); }
};

Binding in XAML (DataContext is a wrapped UExampleViewModel):
<TextBlock Text="{Binding Foo}"/>

Steps: create the objects as above, change the value once, read the target text.
Expected: changed.
Actual: initial (the target is never updated).
Control: calling NoesisNotifyPropertyChanged(ViewModel.Get(), "Foo") afterwards updates the target to changed, so the binding and the getter work fine. Only the automatic FieldNotify mapping is missing.

PlatformWindows

Relationships

related to 0005279 assignedhcpizzi Inherited FieldNotify members do not update 

Activities

njahs

njahs

2026-10-25 01:42

reporter   ~0012789

Proposed fix: In the getter pass, when the getter function has no field id, look for the backing property and register its field id under the same binding name. In editor builds the property whose Getter metadata names the function is used. In all builds the property called Foo or bFoo is used.

proposed-fix.patch (1,371 bytes)   
--- a/Plugins/NoesisGUI/Source/NoesisRuntime/Private/NoesisTypeClass.cpp
+++ b/Plugins/NoesisGUI/Source/NoesisRuntime/Private/NoesisTypeClass.cpp
@@ -3141,6 +3141,42 @@
 						{
 							TypeClass->AddFieldNotification(FieldId, FName(PropertyId.Str()));
 						}
+						else
+						{
+							FProperty* BackingProperty = nullptr;
+							for (TFieldIterator<FProperty> BackingIt(Class, EFieldIteratorFlags::ExcludeSuper); BackingIt; ++BackingIt)
+							{
+								FProperty* Candidate = *BackingIt;
+#if WITH_EDITORONLY_DATA
+								const FString* GetterMeta = Candidate->FindMetaData(TEXT("Getter"));
+								if (GetterMeta != nullptr && *GetterMeta == FunctionName)
+								{
+									BackingProperty = Candidate;
+									break;
+								}
+#endif
+								const FString CandidateName = Candidate->GetName();
+								if (CandidateName == FString(PropertyId.Str()) || CandidateName == FString(TEXT("b")) + FString(PropertyId.Str()))
+								{
+									BackingProperty = Candidate;
+								}
+							}
+							if (BackingProperty != nullptr)
+							{
+								auto BackingFieldId = Descriptor.GetField(Class, BackingProperty->GetFName());
+								if (BackingFieldId.IsValid())
+								{
+									TypeClass->AddFieldNotification(BackingFieldId, FName(PropertyId.Str()));
+								}
+							}
+						}
 					}
 				}
 			}
proposed-fix.patch (1,371 bytes)   

Issue History

Date Modified Username Field Change
2026-10-25 01:40 njahs New Issue
2026-10-25 01:41 njahs Description Updated
2026-10-25 01:42 njahs Note Added: 0012789
2026-10-25 01:42 njahs File Added: proposed-fix.patch
2026-10-26 11:16 sfernandez Assigned To => hcpizzi
2026-10-26 11:16 sfernandez Status new => assigned
2026-10-26 11:16 sfernandez Target Version => 4.0.2
2026-10-26 11:18 sfernandez Relationship added related to 0005279