IPropertyTypeCustomization GetValue returning fixed value when property is set to something else

Here is my IPropertyTypeCustomization subclass:

Header:

#pragma once   
#include "IPropertyTypeCustomization.h"
#include "Editor/PropertyEditor/Public/PropertyHandle.h"
#include "Editor/PropertyEditor/Public/DetailWidgetRow.h"
#include "BMove/Classes/Common/TaskSceneManager.h"
#include "Runtime/Slate/Public/Widgets/Input/STextComboBox.h"
#include "TaskComponentData.h"
 

// Delegate for when this property editor is fully initialized
DECLARE_DELEGATE_OneParam(ReadyDelegate, FTaskComponentDataCustomization *);

// Delegate for when a value has been changed
DECLARE_DELEGATE_OneParam(ValueChangedDelegate, FString);

DECLARE_DELEGATE(OnDestructDelegate);

class FTaskComponentDataCustomization : public IPropertyTypeCustomization
{

public:

	static TSharedRef<IPropertyTypeCustomization> MakeInstance();
 
	FTaskComponentDataCustomization::~FTaskComponentDataCustomization();

	/** IPropertyTypeCustomization interface */
	virtual void CustomizeHeader(TSharedRef<class IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
	virtual void CustomizeChildren(TSharedRef<class IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
	
	void SetTaskData(TArray<FString> AvailableTasks, FString DefaultTask = "");
	void SetTaskSelection(FString selection);

	ReadyDelegate										MyReadyDelegate;
	ValueChangedDelegate								MyValueChangedDelegate;
	OnDestructDelegate									MyDestructionDelegate;

	uint32												TaskComponentIdentifier;

	void												SelectionChanged(TSharedPtr < FString > str, ESelectInfo::Type type);

	bool												bIsReady;

private:

	TSharedPtr<IPropertyHandle>							UPropertyTaskNameHandle;
	TSharedPtr<IPropertyHandle>							UPropertyTaskComponentIdentifierHandle;
	TSharedPtr<IPropertyHandle>							UPropertyTaskComponentNameHandle;

	// Class specific members

	TSharedPtr<STextComboBox>							ComboBoxPtr;

	TArray< TSharedPtr<FString>>						TaskNames;

	/** Visibility delegate for the various methods of calculating magnitude */
	EVisibility GetPropertyVisibility(UProperty* InProperty) const;
};

Source:

#include "../BMovePrivatePCH.h"

#include "BMove/Classes/Components/TaskDataCustomization.h"

#include "Runtime/Slate/Public/Widgets/Input/STextComboBox.h"
#include "DetailWidgetRow.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"

#define LOCTEXT_NAMESPACE "TaskDataCustomization"

FTaskComponentDataCustomization::~FTaskComponentDataCustomization() {
	UPropertyTaskNameHandle.Reset();
	UPropertyTaskComponentIdentifierHandle.Reset();
	ComboBoxPtr.Reset();

	TaskNames.Empty();

	MyReadyDelegate.Unbind();
	MyValueChangedDelegate.Unbind();

	if (MyDestructionDelegate.IsBound())
	{
		MyDestructionDelegate.Execute();
	}

}


#pragma region IPropertyTypeCustomization

TSharedRef<IPropertyTypeCustomization> FTaskComponentDataCustomization::MakeInstance()
{
	return MakeShareable(new FTaskComponentDataCustomization());
}

void FTaskComponentDataCustomization::CustomizeHeader(TSharedRef<class IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
	uint32 NumChildren;
	StructPropertyHandle->GetNumChildren(NumChildren);

	for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
	{
		const TSharedRef< IPropertyHandle > ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef();

		if (ChildHandle->GetProperty()->GetName() == TEXT("Names"))
		{
			UPropertyTaskNameHandle = ChildHandle;
		}

		if (ChildHandle->GetProperty()->GetName() == TEXT("AssociatedTaskComponent")) {
			UPropertyTaskComponentIdentifierHandle = ChildHandle;
		}

		if (ChildHandle->GetProperty()->GetName() == TEXT("AssociatedTaskComponentName")) {
			UPropertyTaskComponentNameHandle = ChildHandle;
		}

	}

	check(UPropertyTaskComponentIdentifierHandle.IsValid());
	check(UPropertyTaskNameHandle.IsValid());
	check(UPropertyTaskComponentNameHandle.IsValid());

	FName name;
	UPropertyTaskComponentNameHandle->GetValue(name);

	uint32 testnum = 0;
	UPropertyTaskComponentIdentifierHandle->GetValue(testnum);

	TaskComponentIdentifier = FCString::Atoi(*name.ToString());

	HeaderRow.NameContent()
		[
			StructPropertyHandle->CreatePropertyNameWidget(LOCTEXT("Task", "Task Names"), LOCTEXT("IDK", "IDK"), false, true, false)
		]
	.ValueContent()
		.MinDesiredWidth(250)
		[
			SNew(STextComboBox)
			.OptionsSource(&TaskNames)
		.OnSelectionChanged(this, &FTaskComponentDataCustomization::SelectionChanged)
		];

	ComboBoxPtr = StaticCastSharedRef<STextComboBox>(HeaderRow.ValueWidget.Widget);

	// Register this with TaskComponent
	if (TaskSceneManager::GetInstance()->HookTaskPropertyWithComponent(*this)) {
		MyReadyDelegate.Execute(this);
	}

	bIsReady = true;

}
 
void FTaskComponentDataCustomization::CustomizeChildren(TSharedRef<class IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
}
 
#pragma endregion

void FTaskComponentDataCustomization::SetTaskData(TArray<FString> AvailableTasks, FString DefaultTask) 
{

	TaskNames.Empty();

	for (FString TaskName : AvailableTasks) {
		TaskNames.Add(TSharedPtr<FString>(new FString(*TaskName)));
	}

	if (ComboBoxPtr.IsValid()) {
		ComboBoxPtr->RefreshOptions();
	}

	if (!DefaultTask.IsEmpty()) {
		for (auto &elem : TaskNames) {
			if (elem->Equals(DefaultTask)) {
				ComboBoxPtr->SetSelectedItem(elem);
				break;
			}
		}
	} else {
		ComboBoxPtr->SetSelectedItem(TaskNames[0]);
	}
}

void FTaskComponentDataCustomization::SetTaskSelection(FString selection) {

	ComboBoxPtr->SetSelectedItem(TSharedPtr<FString>(new FString(*selection)));

}

EVisibility FTaskComponentDataCustomization::GetPropertyVisibility(UProperty* InProperty) const {
	return EVisibility::Visible;
}


void FTaskComponentDataCustomization::SelectionChanged(TSharedPtr<FString> str, ESelectInfo::Type type) {

	if (MyValueChangedDelegate.IsBound() && str.IsValid()) {
		MyValueChangedDelegate.Execute(*str);
	}

	if (ComboBoxPtr.IsValid()) {
		ComboBoxPtr->RefreshOptions();
	}

}


#undef LOCTEXT_NAMESPACE

I register this customization with the property editor module and set it up correctly…
This Customization subclass is used for this Struct:

USTRUCT()
struct FTaskComponentData
{
	GENERATED_BODY()

	UPROPERTY(VisibleAnywhere)
	TArray<FString>											Names;

	UPROPERTY(EditAnywhere)
	uint32													AssociatedTaskComponent;

	UPROPERTY(VisibleAnywhere)
	FName													AssociatedTaskComponentName;

};

Which is used in a component.

UPROPERTY(VisibleAnywhere, Category = "Planning | Task")
	FTaskComponentData								AvailableTaskNames;					

Next, I try to set up some default values…

ComponentIdentifier = UTaskComponent::index;
AvailableTaskNames.AssociatedTaskComponent = ComponentIdentifier;
AvailableTaskNames.AssociatedTaskComponent = ComponentIdentifier;
AvailableTaskNames.AssociatedTaskComponentName = FName(TEXT("test"));

But in my FTaskComponentDataCustomization, when I try to get the value for the handle for the AssociatedTaskComponentName like this:

FName name;
UPropertyTaskComponentNameHandle->GetValue(name);

name is for some reason “3”. It changes values some times and it is very erratic behavior. Some other times it is 2. What could be the cause of this?

Hey jchen114-

I tried setting up a custom IPropertyTypeCustomization class based on the code you provided but I was not able to reproduce the results you mentioned. If possible, could you provide a sample project that demonstrates the GetValue returning the incorrect value to help me investigate the issue locally? Additionally, does the GetValue return the same incorrect value in the latest engine version (4.17.2 or the 4.18 preview)?