Resources
Difference between CustomizeHeader and CustomizeChildren

Widget Classes
SPropertyNameWidget: Made byFPropertyHandleBase::CreatePropertyNameWidgetSPropertyEditorTitle
SPropertyValueWidget: Made byFPropertyHandleBase::CreatePropertyValueWidget- Pick in order:
SPropertyEditorArraySPropertyEditorSetSPropertyEditorMapSPropertyEditorOptionalSPropertyEditorClassSPropertyEditorStructSPropertyEditorAssetSPropertyEditorNumericSPropertyEditorComboSPropertyEditorEditInlineSPropertyEditorTextSPropertyEditorBoolSPropertyEditorArrayItemSPropertyEditorDateTimeSPropertyEditorusesFPropertyEditor::GetValueAsText→FPropertyHandleBase::GetValueAsFormattedText→FPropertyNode::GetPropertyValueText→FPropertyTextUtilities::PropertyToTextHelper→FProperty::ExportText_Direct
Miscs
PropertyHandle
PropertyHandle in CustomizeHeader and CustomizeChildren is always the root property (for example, your struct).
Get/Set property
This works if the property has a Edit specifier
TSharedPtr<IPropertyHandle> SelectedSocketProp = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FFUPickerSockets, SelectedSocket));
// Get
FName SelectedSocket;
SelectedSocketProp->GetValue(SelectedSocket);
// Set
SelectedSocketProp->SetValue(FName("NewValue"));CustomizeChildren
This will construct default child members for the member props of your type details customization.
// Thanks to aquanox for the snippet
void FCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
uint32 NumberOfChild = 0;
if (PropertyHandle->GetNumChildren(NumberOfChild) == FPropertyAccess::Success)
{
for (uint32 Index = 0; Index < NumberOfChild; ++Index)
{
TSharedRef<IPropertyHandle> ChildPropertyHandle = PropertyHandle->GetChildHandle(Index).ToSharedRef();
ChildBuilder.AddProperty(ChildPropertyHandle);
}
}
}Custom ReadOnly/Editable override
Taking the same base snippet as above but we set some specific properties to be readonly (while having EditAnywhere metadata)
uint32 NumberOfChild = 0;
if (PropertyHandle->GetNumChildren(NumberOfChild) == FPropertyAccess::Success)
{
TArray<FProperty*> MakeConst = {
CachedStructRowCountPropertyHandle->GetProperty(),
CachedStructColumnCountPropertyHandle->GetProperty(),
CachedStructCellsDataPropertyHandle->GetProperty()
};
for (uint32 Index = 0; Index < NumberOfChild; ++Index)
{
TSharedPtr<IPropertyHandle> ChildPropertyHandle = PropertyHandle->GetChildHandle(Index);
auto& DetailRow = FragmentGroupBuilder.AddPropertyRow(ChildPropertyHandle.ToSharedRef());
if (MakeConst.Contains(ChildPropertyHandle->GetProperty()))
{
DetailRow.IsEnabled(false);
}
}
}Get context
You may want to know in what BP/Actor this property is being rendered Here is how aquanox does for his BlueprintComponentReference plugin
Add new rows
See ChildBuilder.AddCustomRow
Containers
With TMap you can use AsMap on IPropertyHandle.
Then GetElement to get a “Pair”.
You can then get they key with GetKeyHandle
Use GetValueData do get the value data.
Example on how to find back a property handle from a key.
uint32 NumChilds;
CachedStructCellsDataPropertyHandle->GetNumChildren(NumChilds);
for (uint32 ChildIndex = 0; ChildIndex < NumChilds; ++ChildIndex)
{
TSharedRef<IPropertyHandle> EntryHandle = CachedStructCellsDataPropertyHandle->AsMap()->GetElement(ChildIndex);
TSharedPtr<IPropertyHandle> KeyHandle = EntryHandle->GetKeyHandle();
void* KeyRawData;
KeyHandle->GetValueData(KeyRawData);
FIntVector3* KeyPtr = static_cast<FIntVector3*>(KeyRawData);
if (*KeyPtr == Coords)
{
return EntryHandle;
}
}