[UE5.5]How to create a TextBlock dynamically

I need to create an array of TextBlocks dynamically at runtime. I don’t know how many until runtime. Is there a way to do this? TextBlock is not a UserWidget, so I can’t use CreateWidget.

If it like a dynamic list, you can create a child widget than contain only the text block and then a main widget with an event to create that child widget inside a vertical box for example.

If it not a list, then just an array of Text.

I was trying to avoid a custom widget because then I have to pass through all the settings. Child widgets aren’t possible because CreateWidget only works with UserWidget and TextBlock isn’t a UserWidget.

You can use Construct Object from class to create it.

image

image

Woah! That worked perfectly. Thanks!

1 Like

Hey bro.
There is if you can communicate somehow to that class how many of those UTextBlock you want.
This how I would do it:
If you can do it in the class itself, override NativeConstruct() or BeginPlay() to do it.
If you can’t and have to receive input from another class.
Either way, the method you need to handle that logic should look something like:

UFUNCTION(BlueprintCallable)
bool TryGenerateTextBlocks(int32 AmountToGenerate, TArray<UTextBlock*>& OutTextBlockArray, bool bClearCurrentArray = false);

(Or something similar in Blueprints)
Whatever the approach you may wanna choose, your implementation would look something like this:

bool MyActor::TryGenerateTextBlocks(int32 AmountToGenerate, TArray<UTextBlock*>& OutTextBlockArray, bool bClearCurrentArray)
{
	/*
	Since you said that the array could be any size, it could also be empty. 
	That being you will need a way to check if the method was properly executed or not. This return should tell you that.
	*/
	bool bWasArrayGenerated = false;

	if (AmountToGenerate <= 0)
	{
		UE_LOG(LogTemp, Log, TEXT("MyActor::TryGenerateTextBlocks: Method was called but the requested amount in AmountToGenerate was '<= 0.'"));
		bWasArrayGenerated = true;
		return bWasArrayGenerated; // Returning True because technically the amount requested was the amount created.
	}

	if (!OutTextBlockArray.IsEmpty()) // Check if the OutTextBlockArray has records in it.
	{
		UE_LOG(LogTemp, Log, TEXT("MyActor::TryGenerateTextBlocks: OutTextBlockArray was not empty."));

		if (bClearCurrentArray)  // Clearing the OutTextBlockArray if needed.
		{
			OutTextBlockArray.Empty();
			UE_LOG(LogTemp, Log, TEXT("MyActor::TryGenerateTextBlocks: OutTextBlockArray cleared."));
		}
	}

	int32 InitialTextBlockArrayAmount = OutTextBlockArray.Num(); // This will be useful to valid if the process was successful later on.
		
	// Update the loop if you are not looking for <= amount of generated UTextBlocks.
	for (int32 i = 0; i <= AmountToGenerate; i++)
	{
		TObjectPtr<UTextBlock> NewTextBlock = NewObject<UTextBlock>(this); // Dynamically create a UTextBlock.
		
		if (NewTextBlock) // Check if NewTextBlock is a valid pointer.
		{
			/*
				In this space you can update the NewTextBlock however you like. The Text, Size, etc. 
				Remember to do this even for testing since if you just create them they will have no visual representation whatsoever.
			*/

			OutTextBlockArray.Add(NewTextBlock); // Add it to the array.
		}
	}

	// Validate if the process has occured as intented.
	if (OutTextBlockArray.Num() == InitialTextBlockArrayAmount + AmountToGenerate)
	{
		UE_LOG(LogTemp, Log, TEXT("MyActor::TryGenerateTextBlocks: Text blocks array was generated with %i records"), OutTextBlockArray.Num());
		bWasArrayGenerated = true;
	}
	else
	{
		UE_LOG(
			LogTemp, 
			Warning, 
			TEXT("MyActor::TryGenerateTextBlocks: Could not generate all record for the array. Initial Amount: %i / Amount requested: %i / Final amount: %i"), InitialTextBlockArrayAmount, AmountToGenerate, OutTextBlockArray.Num()
		);
		bWasArrayGenerated = false;
	}

	return bWasArrayGenerated;
}

But here are a few thing you have to keep in mind here.

  1. Generating your UTextBlocks on your Widget outside of a wrapper like a Panel can go really bad. Try to wrap them after generated on a WrapBox or similar.
  2. Creating UTextBlocks like this is not really performance-wise. Specially done during gameplay. I would recomend you to create another class based on UUserWidget, bind a UTextBlock property of that class, create a Widget Blueprint for this new UUserWidget, adjust every visual element of every single TextBlock there, create a property in your class that is a TSubclassOf<> that new UUserWidget created, fill it up in your blueprint editor with the new Widget Blueprint, and all done. You are not creating anything in gameplay from scrach, your are just duplicating a object that is already instantiated. Way better for performance.
  3. Clamp. The. Amount. I don’t know how your logic of how many UTextBlocks need to be created works, but if there’s a possibility of creating like 1000 UTextBlocks, clamp it. This would not just be bad, this could tank your performance. Seriously.

Anyway. Hope I could help.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.