How to update a customized details panel?

I have a custom property layout which should (in theory) display a variable number of children based on this property’s value. An adjustable length array, so to speak. So, my CustomizeChildren generates an appropriate number of elements, but I have to push Recompile to update the details panel after changing this value.
I can call any function when it changes using IPropertyHandle::SetOnPropertyValueChanged. So, which function is used to refresh the details view?

I’ve tried NotifyPostChange and some other stuff but none of it works.
I am sure this is possible - after all, TArray does work. But I can’t find its customization code, which would make my life easier. I haven’t tried to make full class details customizations yet, but I will if it’s necessary.

As a side question, where does the “reset to default” button come from? I want to either disable it, or make it reset to my values rather than being completely empty.

Some pictures:

Nicely generated at first

Changed ItemClass to new value

52044-varnum2.png

After pressing Compile

52045-varnum3.png

I don’t think you can do this with an IPropertyTypeCustomization. It looks like you want to customize one property based on the value of another? If so, you should do that through an IDetailCustomization for the containing class instead.

You’ll get a reference to an IDetailLayoutBuilder, which you can store and later call ForceRefreshDetails on to have the custom layout regenerated.

As for the reset to default, that has a very specific meaning which you shouldn’t try to change - it resets the property to the value specified in the class default object of the given instance’s class (or for a blueprint, to the value from the parent class). So if its behaviour isn’t what you expect, you should probably be putting the default values you want inside the C++ class constructor.

Old question, but I was needing the same thing and found this to work when needing a total refresh of the Details Panel…

FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyEditorModule.NotifyCustomizationModuleChanged();
3 Likes

after almost 3 years from the original question - but there is a solution.
Get the parent category, and then the parent layout. Then just refresh.

    IDetailCategoryBuilder &ParentCategory = StructBuilder.GetParentCategory();
    IDetailLayoutBuilder &LayoutBuilder = ParentCategory.GetParentLayout();
    LayoutBuilder.ForceRefreshDetails();
1 Like

Hi. I’m newbie in editor code. How can I get StructBuilder?

I think in the source above, StructBuilder is probably the IDetailChildrenBuilder& passed in to CustomizeChildren in your derived IPropertyTypeCustomization class.

^ this works perfectly. Put it in your Actor::PostEditChangeProperty() function. Don’t even have to #include anything.

1 Like

There is an another solution that I personally consider best and a good practice:

The IPropertyTypeCustomizationUtils type passed to the CustomizeHeader method contains a IPropertyUtilities facility via the GetPropertyUtilities method. This can be passed directly to your OnChange methods that should trigger the refresh as it in turn has the ForceRefresh() method.

This is how I do it:

void FTraitEntryCustomization::OnTypeChanged(TSharedPtr<IPropertyHandle> TypePropertyHandle,
									 TSharedPtr<IPropertyUtilities> Utils)
{
  if (Utils)
  {
	Utils->ForceRefresh();
  }

Later:

void FMyStructCustomization::CustomizeHeader(
TSharedRef<IPropertyHandle>      StructPropertyHandle,
class FDetailWidgetRow&          HeaderRow,
IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
  TSharedPtr<IPropertyHandle> TypePropertyHandle =
	  StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyStruct, Type));
  OnTypeChanged(TypePropertyHandle, nullptr);

  TypePropertyHandle->SetOnPropertyValueChanged(
  	FSimpleDelegate::CreateSP(this, &FMyStructCustomization::OnTypeChanged,
							  TypePropertyHandle,
							  StructCustomizationUtils.GetPropertyUtilities()));

Note the first in-header call is passing nullptr to skip the first call to refresh on Widget creation. An assertion is raised otherwise.

Thanks for this, holding onto the IPropertyUItilities was hitting a check(). Somehow passing it as a delegate param works fine.