UMG UButton OnClicked.AddDynamic with extra parameters?

Hi, I’m trying to create dynamic buttons at runtime, but i need to pass a varable to the onClick function. This is my actual code:




// THE CODE THAT CREATE BUTTONS IN MY FUNCTION
...
for (auto& item : LoadedData.Items)
{
	UButton *button = NewObject<UButton>(UButton::StaticClass());

	FString ImagePath = item.Texture;
	UTexture2D* Texture = Cast<UTexture2D>(StaticLoadObject(UTexture2D::StaticClass(), NULL, *(ImagePath)));

	FSlateBrush itemBrush = UWidgetBlueprintLibrary::MakeBrushFromTexture(Texture);
	itemBrush.ImageSize.X = 200;
	itemBrush.ImageSize.Y = 200;
	itemBrush.DrawAs = ESlateBrushDrawType::Image;

	FButtonStyle style;
	style.SetNormal(itemBrush);
	style.SetHovered(itemBrush);
	style.SetPressed(itemBrush);

	button->WidgetStyle = style;

	button->OnClicked.AddDynamic(this, &UDetailsPanelWidget::WriteLog);

	itemsBox->AddChild(button);
}
...
// THE ONCLICK FUNCTION
void UDetailsPanelWidget::WriteLog()
{
	UE_LOG(LogTemp, Warning, TEXT("*** BUTTON CLICKED"));
}


If I want to pass a FString to my WriteLog function, how I can do that? I’ll try with no success a method like this:



// IN THE FOR LOOP
...
button->OnClicked.AddDynamic(this, &UDetailsPanelWidget::WriteLog, item.Title);
...

// THE WriteLog Function
void UDetailsPanelWidget::WriteLog(FString title)
{
      UE_LOG(LogTemp, Warning, TEXT("*** BUTTON MESSAGE: %s"), title);
}



Thanks a lot.

The OnClicked delegate doesn’t take any additional parameters so you can’t pass anything else in. It’s like trying to send too many parameters to a function, it won’t work or probably even compile. You’ll have to find another way around it, since creating your own delegate isn’t really an option here without overriding engine source and causing yourself a lot of headaches.

I suspect badstorm has used standard delegates before, hence the confusion. Whilst you can bundle whatever you want into normal delegate bindings, dynamic delegates cannot take any kind of payload. I’m guessing it’s mostly because they need to support serialization, and with arbitrary attached data, that would be a nightmare to implement.

So anyway, unfortunately there is no nice way around this that I’m aware of. I came up with a workaround, it’s quite a pain in the *** but if you really need it…

Basically, you can create an array of proxy UObjects to route the callback through. Create a UObject class which contains data members for whatever data you need to attach, along with a method matching the delegate signature (in this case, no params, no return value). Also give it a pointer to your object, in this case a UDetailsPanelWidget*. You’ll need to store these proxy objects somewhere, perhaps a TArray within your widget class. In this case, I’d guess you might instead want to pack an object pointer into your item structure, since there’ll be a 1:1 correspondence. Finally, in the loop above, you create a new proxy object for each item, store the relevant data in it, set it’s pointer to your panel widget, and hook the delegate up to it.



item.MyProxy = NewObject< UMyProxyObject >(this);
item.MyProxy->OwnerPtr = this;
item.MyProxy->StringData = item.Title;
button->OnClicked.AddDynamic(item.MyProxy, &UMyProxyObject::MyProxyHandler);


And finally, in the handler, just route the call back to the widget, along with the attached data:



void UMyProxyObject::MyProxyHandler()
{
  OwnerPtr->WriteLog(StringData);
}


Easy, huh? :expressionless:

Now, having said all that, if you’re creating custom widgets, you’re generally much better off doing all your customization in Slate. Then you can just wrap your custom Slate widget with a UMG wrapper to expose it to the designer. In Slate, you’d be able to use regular delegates and wouldn’t need this ugly workaround.

And if anyone knows of a less horrible way to achieve the above functionality, please tell me!

Thanks for your suggestion. At the end I create a custom UButton with a fuction that require data as parameters and that set it in a internal variable and set the OnClick fucntion that call this variable.

Do you know where i can find some documentatio about wrapping my slate widget with a ugm wrappers? Thnaks

Don’t think there is any, but it’s fairly straightforward. Just look how it’s done in the engine code for some of the regular widgets. I think when I first did it I based it off of the UImage widget, Image.h/.cpp in Engine/Source/Runtime/UMG/Public(or Private for cpp)/Components.

I tried your method but it did not work.
when I click the button, the callback function MyProxyHandler was not called …