One of the most common questions you’ll have if you start making C++-based UIs is this:
How can I control Blueprint-created widgets from C++?
The answer to this is the BindWidget
meta property.
Basic BindWidget example
Connecting C++ to Blueprints through BindWidget.
UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
class UTextBlock* ItemTitle;
While it’s not mentioned on the UPROPERTY()
wiki
page or any of the other
documentation,
it’s one of the most useful tags for you as a UI developer.
By marking a pointer to a widget as BindWidget
, you can create an
identically-named widget in a Blueprint subclass of your C++ class, and at
run-time access it from the C++.
Here’s a step-by-step process to getting a test working:
- Create a C++ subclass of
UUserWidget
. - In it add a member variable of type
UWidget*
, or the UWidget subclass that you wish to access from C++ (UImage
,UTextBlock
etc.) - Mark it with
UPROPERTY(meta=(BindWidget))
. - Run the editor and create a Blueprint subclass of your C++ class.
- Create a widget with the same type and exact name as your member variable.
- You can now access the widget from C++.
If any of these don’t make sense, check out my introductory series on making UIs with Unreal.
Example Code
BindExample.h
Header file for our bind example.
#pragma once
#include "BindExample.generated.h"
UCLASS(Abstract)
class UBindExample : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget))
class UTextBlock* ItemTitle = nullptr;
};
BindExample.cpp
In the body we can also reference the property
#include "BindExample.h"
#include "Components/TextBlock.h"
void UBindExample::NativeConstruct()
{
// Call the Blueprint "Event Construct" node
Super::NativeConstruct();
// ItemTitle can be nullptr if we haven't created it in the
// Blueprint subclass
if (ItemTitle)
{
ItemTitle->SetText(TEXT("Hello world!"));
}
}
Now compile the C++ and open up your Blueprint subclass of the C++ class where
you added ItemTitle
. If you compile your Blueprint, you will be shown an
error if there is no TextBlock widget named ItemTitle
inside your UserWidget.
Pros & Cons of BindWidget
- Easier to maintain complex logic in C++. No spaghetti-fighting in Blueprints.
- Easier for collaboration, no worries about locking Blueprint assets.
- Requires re-compile to see changes.
- Can be harder for non-programmers to see how data is being populated.
Optional Widgets
If you want to make the widget optional, instead use meta=(BindWidgetOptional)
.
With this there will be no error shown if the Blueprint class does not have
a widget with that name.
BindWidgetOptional example
UPROPERTY(BlueprintReadWrite, meta = (BindWidgetOptional))
UTextBlock* ItemTitle;
Quirks of BindWidget
- Even if you don’t mark the widget as optional in your property tags, you can
still run your game with a Blueprint subclass that has not defined
a matching widget. So you should generally do
nullptr
checks. - Normally when ticking the “Is Variable” flag on a UserWidget would make it
available in the Graph tab of the editor. However, if there is a property
with that name, marked as
BindWidget
, the only way to make it accessible in the blueprint is addBlueprintReadOnly
orBlueprintReadWrite
to itsUPROPERTY()
tag. - By default, variables defined in a parent C++ class are only shown in the Variables list if “Show Inherited Variables” is checked (see screenshot).
- Widgets marked with
BindWidget
are null in the C++ constructor, they are initialized later on in the lifecycle. If you need to do constructor-like setup use theNativeConstruct()
function.
How to show variables defined in a parent C++ class.
What next?
- You can combine this with the
SynchronizeProperties
function to create UserWidgets whose appearance updates dynamically in the editor.