I am trying to spawn a widget at runtime as part of a spatial inventory, to do this i have made a couple of custom widget classes extending from user widget. The first of which defines my grid and is working correctly, the problem is with my ContainerGrid_Item class.
When the player walks near an item the ContainerGrid class creates a ContainerGrid_Item and positions it on the grid in a valid position and then calls RedrawWidget. You can see this code below:
void UContainerGrid::RedrawInventory()
{
GridCanvasPanel->ClearChildren();
//Get all items in container
TMap<FItemDataModel*,FContainerTile> ItemsToDraw = GetAllItems();
for (TTuple<FItemDataModel*, FContainerTile> kvp : ItemsToDraw)
{
if (UContainerGrid_Item* newItem = CreateWidget<UContainerGrid_Item>(GridCanvasPanel, UContainerGrid_Item::StaticClass()))
{
//Tried overriding these base methods to force the re-binding of the various widgets
newItem->NativeConstruct();
newItem->SynchronizeProperties();
newItem->RebuildWidget();
newItem->OnRemoved.AddUniqueDynamic(this,&UContainerGrid::RemoveItem);
//I slot this new widget as a canvas slot
if(UCanvasPanelSlot* PanelSlot = GridCanvasPanel->AddChildToCanvas(newItem))
{
//Size the panel
PanelSlot->SetAutoSize(true);
PanelSlot->SetPosition(FVector2D(kvp.Value.X * TileSize, kvp.Value.Y * TileSize));
//Provide important values
newItem->AssignValues(TileSize, kvp.Key);
newItem->RedrawWidget(); //<===== This is the broken method
UE_LOG(LogTemp, Warning, TEXT("Container Grid Attempts to do thing now"))
}
}
}
}
I think i understand why this problem is arising, It is probably due to the widgets not being bound in the next frame resulting in null pointers in the RedrawWidget method below:
void UContainerGrid_Item::RedrawWidget()
{
ItemSize = FVector2D(ItemData->ItemDimensions.X * TileSize, ItemData->ItemDimensions.Y * TileSize);
//Set Size Box Size
BackgroundSizeBox->SetWidthOverride(ItemSize.X); //BackgroundSizeBox is null
BackgroundSizeBox->SetHeightOverride(ItemSize.Y);
//Set Size of Image
if(UCanvasPanelSlot* CanvasSlot = ParentCanvas->AddChildToCanvas(ItemImage)) //Parent Canvas is null
{
CanvasSlot->SetSize(ItemSize);
}
//Set Image
ItemImage->SetBrush(GetItemWidget()); //ItemImage is null
}
For added clarity my widgets are declared in the header like this:
UPROPERTY(EditAnywhere, meta = (BindWidget))
TObjectPtr<UCanvasPanel> ParentCanvas;
UPROPERTY(EditAnywhere, meta = (BindWidget))
TObjectPtr<USizeBox> BackgroundSizeBox;
UPROPERTY(EditAnywhere, meta = (BindWidget))
TObjectPtr<UBorder> BackgroundBorder;
UPROPERTY(EditAnywhere, meta = (BindWidget))
TObjectPtr<UImage> ItemImage;
Am i grossly miss-using UUserWidget extened classes or is there a way to ensure my bindings are assigned at the point of use?