Unity’s UI Toolkit is definitely a love-it or hate-it type of deal (and even when you love it, you will absolutely find ways and days to hate it!), but there’s no denying the ability to strongly define your UI controls and create reusable building blocks with common reactions and styles is an incredibly powerful one. Unity also provides the ability to reuse common UXML snippets as templates, and lets developers override certain template tags on a per-instance basis.
One of the most powerful features in UXML templates is the ability to specify where to nest child elements inside templates, using the content-container attribute. From Unity’s docs, define a template like below:
<ui:UXML xmlns:ui="UnityEngine.UIElements" > <ui:Label text="Group Title" name="groupTitle" /> <ui:VisualElement name="parent-container" content-container="anyValue"> <!--Add child elements here --> </ui:VisualElement> <ui:VisualElement /></ui:UXML>
then nest content as so:
<ui:UXML xmlns:ui="UnityEngine.UIElements"> <ui:Template src="MyTemplate.uxml" name="my-template"/> <ui:Instance template="my-template"> <ui:Label text="Test"/> <!--This label element is instantiated inside the `parent-container` element--> </ui:Instance></ui:UXML>
For custom controls, the VisualElement base class contains its own virtual property that can be overridden to produce this same behavior (the ScrollView control and others make good use of this feature):
/// <summary>/// <para> /// Logical container where child elements are added./// If a child is added to this element, the child is /// added to this element's content container instead./// </para>/// </summary>public virtual VisualElement contentContainer => this;
Combining this with the enjoy-it-while-its-still-here Resources.Load() function, we can create the base class for a nestable template element that doesn’t require serializing the UXML file references elsewhere, allowing us to instantiate new instances directly in C# or the UI Builder.
/// <summary>/// Custom VisualElement inheritor that uses Resources.Load to/// automatically clone itself from template./// </summary>[UxmlElement]public abstract partial class BaseTemplateElement : VisualElement{ private const string ContentContainerClassSuffix = "__content-container"; private static readonly Dictionary<string, VisualTreeAsset> s_cachedTemplates = new(); private VisualElement _contentContainer; protected BaseTemplateElement() { LoadTemplate(ResourcesPath); ApplyAnyDefaultStyle(); SetupContentContainer(); } public override VisualElement contentContainer { get => _contentContainer; } protected abstract string ResourcesPath { get; } protected abstract string DefaultStyleClass { get; } private void ApplyAnyDefaultStyle() { if (DefaultStyleClass == string.Empty) return; AddToClassList(DefaultStyleClass); } private void SetupContentContainer() { var contentContainerClassName = DefaultStyleClass + ContentContainerClassSuffix; _contentContainer = this.Q<VisualElement>(null, contentContainerClassName) ?? this; } /// <summary> /// Looks for a cached template at TemplatePath; /// tries to use Resources.Load to cache it, if not loaded /// </summary> /// <param name="templatePath">/Resources/ subfolder path</param> private void LoadTemplate(string templatePath) { if (!s_cachedTemplates.ContainsKey(templatePath)) s_cachedTemplates[templatePath] = Resources.Load<VisualTreeAsset>(templatePath); if (s_cachedTemplates[templatePath]) s_cachedTemplates[templatePath].CloneTree(this); }}
with the below inheriting class, plus the UXML for the template:
[UxmlElement]public partial class ExampleControl : BaseTemplateElement { protected override string ResourcesPath => "UI/Templates/ExampleControl"; protected override string DefaultStyleClass => "t-example"; }
<ui:UXML xmlns:ui="UnityEngine.UIElements"> <ui:VisualElement class="t-example__header"> <ui:VisualElement class="t-example__body t-example__content-container"> <!-- Add() will place Child elements here --> </ui:VisualElement> </ui:VisualElement></ui:UXML>
This can be incredibly helpful as you standardize your reusable UI building blocks, allowing you to create a common UI language first, and then build components on top of that. In the image below, the custom controls under Templates handle look & feel, plus possibly some basic generalized functionality (NavigableScrollView is a ScrollView that can have VisualElements added to it, which will behave as items in a list when scrolling/navigating with the keyboard or controller). You can scaffold your custom controls from there, using a combination of these template controls and Unity’s own built-in controls.

Mixing it all together to create a coherent-looking UI ; A BoxTemplate is a VisualElement that can have a background, frame, and/or outline. A HeadingTemplate contains a Label inside a BoxTemplate; A CardTemplate contains a HeadingTemplate inside another BoxTemplate, with room for content after the Heading; The RecipeInfoCard adds an IconTemplate, description Label, and Input and Output IconTemplates to the CardTemplate; and so on, etc.
More next time on the various ways to ensure Unity UI Toolkit is working for you, not against you!

Leave a Reply