Bind OnClicked Events to buttons created at runtime.

Hello Everyone ,

I have scrollbox with buttons created at runtime based on number of .sav files in game folder.

UButton *save_button = NewObject<UButton>(this);
loadgame_scrollbox->AddChild(save_button);

Each button represents one save file, that can be loaded. I’m trying to bind events to that buttons at runtime:

button_interface->OnClicked.AddDynamic(this, &UMasterUI_Menu::OnClick_button_load_game);

This code works, but it’s bad idea to repeat it 100 times with 100 functions (one per button). And I don’t know how many buttons will spawn, it’s based on number of .sav files. I don’t know which button called that function so I don’t know which save file should I load!

Is it possible to return Name/Index of the clicked button with OnClicked? And then use that index in OnClick_button_load_game(int32 index) function to determine which button was clicked. For example OnValueChanged Event returns float Value by default(USlider) and it’s easy to access Value this way.

I need c++ solution, not BP (i’m using c++ only).

1 Like

Hello XadE,

Well what you could try, even if it’s not beautiful, you can wrap a level of indirection on your OnClicked delegate. What I mean by that, you define a new type of delegate that return a value. this new type will do an internal invoke to your another delegate that will call your function load_save.

With that level of indirection you’ll be able to catch the actor the Onclicked came from.

Do you follow me?

gamer08

Sounds good, could you explain how to do that in code?

Thank you. I’ll test this code tomorrow

Hello again XadE, I made an example for you to understand.

Header file

#pragma once

#include "GameFramework/Volume.h"
#include "GameFramework/Actor.h"
#include "DemoDelegateVolume.generated.h"

DECLARE_DELEGATE_OneParam(HelloDelegate, FString);

UCLASS()
class FPS4_8_API ADemoDelegateVolume : public AVolume
{
	GENERATED_BODY()
	
public:
	ADemoDelegateVolume();
	
	UFUNCTION()
	void OnBeginOverlap(AActor* overlappingActor);

	void HelloFromCharacter(FString characterNmae);

	HelloDelegate hello;
};

Source file

    #include "FPS4_8.h"
    #include "DemoDelegateVolume.h"
    #include "Engine.h"
    #include "FPS4_8Character.h"
    
    ADemoDelegateVolume::ADemoDelegateVolume()
    {
    	
    	OnActorBeginOverlap.AddDynamic(this, &ADemoDelegateVolume::OnBeginOverlap);
    	
    	//Bind function
    	hello.BindUObject(this, &ADemoDelegateVolume::HelloFromCharacter);
    }
    
    void ADemoDelegateVolume::OnBeginOverlap(AActor* overlappingActor)
    {
    	AFPS4_8Character* character = Cast<AFPS4_8Character>(overlappingActor);
    	if (character)
    	{
    		hello.Execute(overlappingActor->GetName());
    	}
    }
    
    void ADemoDelegateVolume::HelloFromCharacter(FString characterName)
    {
    	if (GEngine)
    		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString("Hello from character : ").Append(characterName));
    
    }

Now some details.

I did based on a volume.

I defined a new BeginOverlap that I replace the default one with. (maybe not required in your case).

I define a delegate with one parameter in my case a FName.

I’m a delegate instance on my volume to use it.

In my volume constructor I replace the OnOverlap function with my own with the addDynamic macro.

After that I bind the delegate instance to the function with 1 parameter I want ( in your case it will be OnClick_button_load_game with the parameter you want)

After on my own BeginOverlap, I invoke my delegate bind to the function with one parameter as you can see.

For more infos on delegate you can here : Delegate doc

Is my example help you ?

If not, or if you need anything, just tell me.

gamer08
.

It works on events like that:

53342-zrzut+ekranu+2015-08-09+12.58.42.png

but OnClicked doesn’t return anything.

53343-zrzut+ekranu+2015-08-09+13.00.08.png

void UMasterUI_Menu::OnButtonClicked()  //same for all buttons, binded inside while loop at runtime
{
	testdelegate.Execute(/*still no idea how i can get clicked button here*/);
}

Please, help me. Maybe there is another way of doing that.

Hello gamer08,

For now it’s simple UScrollBox with UButtons spawned in c++ code(default class). I have widget blueprint too, with some panel widgets (it’s easier to make good menu design here).

Hello gamer08,

Could you make an example? Thank you for response

Hello XadE,

I have a little question for you, did you made your menu scrollBox in slate code or with blueprints directly?

if so, did you declared a custom class for your buttons in code?

such as

UCLASS()

class LoadButton : public UButton
{

/****/
/****/

};

Depending on your answers, I have different solutions for you.

gamer08

Ok thanks for that info, what I propose you is to create your own UButton subclass (that contains slate stuff) so you will be able to define your delegate attach to the Onclicked Event on your button and with that method, you’ll be able to catch the infos you need and use the code I shown you.

Did you follow me ?

I could also make an example for you if you need (with button this time to stick to your problem)

Just tell me, always glad to help.

gamer08

Ok i’ll do it, give me some time.

gamer08

Hello, i’m building your example and I wanna know your UMasterUI_Menu is a custom class you made?

What’s the type of it? I just want to know a bit of your achitecture to stick as most as possible on your situation.

For the moment I builded a custom UserWidget that has a ScrollBox but before going any further, I’d like to know your actual architecture related to your UI.

Thanks

gamer08

Did you put your UMasterUI_Menu on the gameMode, a customHUD or elsewhere?

Also can you send me a partial header and source (all I want, it’s your constructor and the init) of your UMasterUI_Menu. My example is a bit fuzzy and Don’t want to lost you. If you don’t want, I’ll explain the best I can.

thanks gamer08

Thanks for sharing, i’ll be back soon

It’s custom UUserWidget

UCLASS()
class EASSETH_API UMasterUI_Menu : public UUserWidget

It’s parent class of my widget blueprint which I add to viewport on BeginPlay of my menu pawn.

void UMasterUI_Menu::NativeConstruct()
{
	Super::NativeConstruct();

	menuPhase = EMenuPhaseEnum::VE_Menu;

	MasterUI_Construct();
}

MasterUI_Construct - i use this function to read game video/audio settings from .sav and get ±10 widgets from widget blueprint (GetWidgetFromName).

It’s my prototype function that spawns buttons.

Thanks, XadE

my class exemple is almost done and now I’ve spotted the problem.

Header File

#pragma once

#include "Components/Button.h"
#include "DemoButton.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLoadDelegate, FString, name);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FClickDelegate);

UCLASS()
class FPS4_8_API UDemoButton : public UButton
{
	GENERATED_BODY()

public:
	UDemoButton();

	UPROPERTY()
	FLoadDelegate load;
	
	UPROPERTY()
	FClickDelegate click;

	void OnClick();

	void LoadGameFile(FString name);
};

Source file

#include "FPS4_8.h"
#include "DemoButton.h"

UDemoButton::UDemoButton()
{
	OnClicked.AddDynamic(this, &UDemoButton::OnClick);
	
	//Bind function
	load.AddDynamic(this,&UDemoButton::LoadGameFile);
}

void UDemoButton::OnClick()
{
	load.Broadcast(this->GetName());
}

void UDemoButton::LoadGameFile(FString name)
{
	/** load stuff here **/
}

So each button has his delegate and when you clicked, it will fire up.

You could also make a delegate that return stuff from your button and after call the load delegate from these info.

Is that help you more ?

If not just tell me.

gamer08

load.Broadcast(this->GetName());

I can’t test that today, so I have one more question. This line returns Name of clicked button and sends Name to loadgame function, right?