Check for changed fields in a visual tree.

Hi,

I am trying to find a way to perform this IMGUI-based code using UIElements:

void OnInspectorGUI()
{
    if (GUI.changed)
    {
        //Do something
    }
}

I am trying to achieve to ‘do something’ whenever any of the fields in a visual tree are altered.

UI Toolkit uses events to react to changes in field values. On a field, you can use myField.RegisterValueChangeCallback(…)

I get that. But now how do I check for all the fields in a visual tree without repetition? For example, if I want to mark the scene dirty for any field having its value changed in a custom editor.

If you’re using DataBinding with SerializedObjects, the scene dirtying should be done automatically.

If you don`t use bindings, a workaround you can use now is to register to ChangeEvents at the root of your visual tree as the change events will bubble up the hierarchy after being handled at the target. While you unfortunately need to register to all known types of ChangeEvents, you only need to do it on one element.

// This will come in in the next 20.2 release, sent from PropertyFields
// evt.target refers to the original field that triggered the change.
rootElement.RegisterCallback<SerializedPropertyChangeEvent>((changeEvent) => DirtyStuff());

rootElement.RegisterCallback<ChangeEvent<int>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<bool>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<float>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<double>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<string>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Color>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<UnityEngine.Object>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Enum>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Vector2>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Vector3>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Vector4>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Rect>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<AnimationCurve>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Bounds>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Gradient>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Quaternion>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Vector2Int>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Vector3Int>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<Vector3Int>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<RectInt>>((evt => DirtyStuff()));
rootElement.RegisterCallback<ChangeEvent<BoundsInt>>((evt => DirtyStuff()));

a less than ideal workaround but it should work. We’ll provide a better way to do this in the future

3 Likes

If only ChangeEvent supported typeless override :wink:

1 Like

alternatively, ChangeEvent could be a simple catch-all using the same syntax

looking forward to SerializedPropertyChangeEvent, sounds like it’ll be super useful! though I think an AnyFieldChanged event would be much more useful.

Any updates on the topic?

Is this still the recommended way ?

This has been around for a while now: Unity - Scripting API: UIElements.BindingExtensions.TrackSerializedObjectValue