One way to link a WBP widget to a C++ variable is using the BindWidget
keyword:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> MyButton;
This requires work on the WBP side to create a matching buttons with the same name.
Is it possible to construct buttons and place them in the widget hierarchy using just C++?
- Create a button
- Place it in the hierarchy
- Button shows up in the designer when creating a WBP child class of the C++ class
Not sure about having it show up in the editor. I think I had issues with the widget tree being set up correctly. But here’s what I did at runtime.
Each widget is in a widget tree.
So if you’re in a widget, you can grab its widget tree and create the button.
UButton *myButton = this->WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), FName("My Button"));
check(myButton);
// This can be any container where you want to place the button.
UNamedSlot *widget = Cast<UNamedSlot>(this->GetWidgetFromName(FName("ContainerSlot")));
widget->AddChild(myButton); // You''ll need to set location and/or setup layout.
Hopefully this will get you started. But like I said, I don’t think the widget tree is setup at all in the editor.
1 Like
The code:
bool USettingsMenu::Initialize()
{
bool SuperReturn = Super::Initialize();
CanvasPanel = WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass(), FName("CanvasPanel"));
WidgetTree->RootWidget = CanvasPanel;
Button = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), FName("Button"));
CanvasPanel->AddChild(Button);
return SuperReturn;
}
The variables do show up in the class details pane:
But they do not exist in the hierarchy:
However, there does seem to be a button on the designer. This cannot be interacted with or selected in any way:
The idea was that perhaps this can work like Actors which will create a hierarchy of components and a blueprint subclass can then start with existing elements and just position/style them.
Is meta = (BindWidget)
then the only way to connect the WBP and C++ sides?
I think you’re running into the same issues I had. It’s been a while since I’ve tried this so I don’t remember the exact details. I think the widget tree is being reset in the editor. There should be a way to do it in C++. The engine is in C++ after all. But I was never able to get any further than this. I eventually created a Slot where I wanted my widget to be placed and I create the widget at runtime and dynamically added it to the slot.
Hopefully someone else can help getting you the rest of the way.
1 Like
I asked Grok and it gave some info you may want to try. It says you have to call Modify() on the widgets you created as well as on the package and then set the blueprint as modified.
Here’s an example of what it gave me. You may have to add “UMGEditor” and “UnrealEd” to your project’s modules.
See the last three lines of this example. Note that you may want to move the widget creation to a separate function so that you can call it from both SetupEditorWidgetTree and from NativeConstruct().
#if WITH_EDITOR
void UMyCustomWidget::SetupEditorWidgetTree()
{
// Ensure we have a widget tree
if (!WidgetTree)
{
WidgetTree = NewObject<UWidgetTree>(this, UWidgetTree::StaticClass());
}
// Get the widget blueprint to modify the editor's widget tree
UWidgetBlueprintGeneratedClass* WidgetBPClass = Cast<UWidgetBlueprintGeneratedClass>(GetClass());
if (!WidgetBPClass) return;
UPackage* Package = WidgetBPClass->GetPackage();
UWidgetBlueprint* MainAsset = Cast<UWidgetBlueprint>(Package->FindAssetInPackage());
if (!MainAsset) return;
// Clear existing children in the editor's widget tree
UGridPanel* AssetGridPanel = Cast<UGridPanel>(MainAsset->WidgetTree->FindWidget("GridPanel"));
if (!AssetGridPanel)
{
AssetGridPanel = MainAsset->WidgetTree->ConstructWidget<UGridPanel>(UGridPanel::StaticClass(), FName("GridPanel"));
MainAsset->WidgetTree->RootWidget = AssetGridPanel;
}
else
{
AssetGridPanel->ClearChildren();
}
// Add text blocks to the grid (example: 2x2 grid)
int32 Width = 2;
int32 Height = 2;
for (int32 Y = 0; Y < Height; ++Y)
{
for (int32 X = 0; X < Width; ++X)
{
FString WidgetName = FString::Printf(TEXT("Text_X%d_Y%d"), X, Y);
UTextBlock* Text = MainAsset->WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), FName(WidgetName));
Text->SetText(FText::FromString(WidgetName));
AssetGridPanel->AddChildToGrid(Text, X, Y);
}
}
// Mark the widget tree and blueprint as modified to update the editor
AssetGridPanel->Modify();
MainAsset->Modify();
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(MainAsset);
}
#endif