Unreal UIs and C++: Connecting C++ to UMG Blueprints with BindWidget

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.

UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
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:

  1. Create a C++ subclass of UUserWidget.
  2. In it add a member variable of type UWidget*, or the UWidget subclass that you wish to access from C++.
  3. Mark it with UPROPERTY(meta=(BindWidget)).
  4. Run the editor and create a Blueprint subclass of your C++ class.
  5. Create a widget with the same type and exact name as your member variable, and mark it as a Variable in the Editor.
  6. 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

BindExample.h

#pragma once 

#include "BindExample.generated.h"

UCLASS(Abstract)
class UBindExample : public UUserWidget
{
	GENERATED_BODY()

public:
	virtual void NativeConstruct() override;

	UPROPERTY(BlueprintReadOnly, meta = (BindWidget))
	UTextBlock* ItemTitle = nullptr;
};

BindExample.cpp

#include "BindExample.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 and open up your Blueprint subclass of the C++ class where you added ItemTitle. If you hit compile, you will be shown an error if there is no TextBlock widget named ItemTitle inside your UserWidget.

Now from C++ you can access ItemTitle. Note that it maybe null if the user did not create a Widget with that name, and ignored the error message and hit Play.

Why BindWidget is Great

  • + 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) (or add meta=(BindWidget, OptionalWidget=true)). With this there will be no error shown if the Blueprint class does not have a widget with that name.

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 to mark it in the usual way with BlueprintReadOnly or BlueprintReadWrite.
  • By default properties defined in a parent C++ class are only shown in the Variables list if “Show Inherited Variables” is checked.
  • Widgets marked with BindWidget are null in the C++ constructor, they are initialized later on in the lifecycle. If you need to access them put the code in NativeConstruct.
How to show variables defined in a parent C++ class.
▲ How to show variables defined in a parent C++ class.

What next?