UI Toolkit TextField RegisterValueChangedCallback fires when only label changes (evt.newValue becomes label text)

Hi everyone,

I found a behavior in UI Toolkit that looks unexpected to me, and I want to confirm whether this is a bug or intended event propagation behavior.

I have this sample code:

using UnityEngine.UIElements;

[UxmlElement]
public partial class SampleElement : VisualElement
{
    public SampleElement()
    {
        var textField = new TextField("");
        textField.RegisterValueChangedCallback(evt =>
        {
            UnityEngine.Debug.Log(evt.newValue);
        });
        Add(textField);

        textField.label = "Sample Text Field";
        schedule.Execute(() =>
        {
            textField.label = "121212";
        }).Every(1000);
    }
}

Expected behavior:

  • The callback should run only when the TextField value changes.

Actual behavior:

  • The callback is triggered even when I only change textField.label.
  • The log appears twice in my scene test.
  • Surprisingly, evt.newValue is the label string (for example “121212”), even though I did not edit the input value.

I currently think this might be related to event bubbling from child elements (such as Label) with the same generic ChangeEvent type.

My workaround/fix was to filter events so I only process the event when the target is the TextField itself:

textField.RegisterValueChangedCallback(evt =>
{
    if (!ReferenceEquals(evt.target, textField))
        return;

    UnityEngine.Debug.Log(evt.newValue);
});

Questions:

  1. Is this behavior expected in UI Toolkit, or should this be considered a bug?
  2. Is filtering by evt.target like above the correct and recommended approach?
  3. Is there a better/official way to receive only true TextField value changes without catching label-related ChangeEvent propagation?

Thanks.

Hi,

This behavior is by design. In UI Toolkit, using RegisterValueChangedCallback registers a callback for a generic ChangeEvent<T>.

Because UI Toolkit uses an event propagation system consisting of a trickle-down and a bubble-up phase, change events bubble up through the visual tree hierarchy.

This means a handler on a parent element will receive bubbled events from its descendant controls anytime the generic type matches.
What you are doing is the correct way to handle this if you only want the event for a specific element.