public class TestScript : MonoBehaviour {
public Material material;
}
Here’s the uitk inspector:
[CustomEditor(typeof(TestScript))]
public class TestEditor : Editor {
public override VisualElement CreateInspectorGUI() {
var root = new VisualElement();
var objectField = new PropertyField(serializedObject.FindProperty("material"), "material");
objectField.RegisterValueChangeCallback(evt => Debug.Log("The event was invoked!"));
root.Add(objectField);
Thread.Sleep(5000);
return root;
}
}
The Thread.Sleep is just there to provoke the bug, in the real world the bug was caused by CreateInspectorGUI creating very many (1000-2000) elements.
What happens is that the value change callback is invoked immediately when the inspector is finished building, as a side-effect of Bind() being called on the SerializedObject.
This does not happen if the Sleep is not there and the editor finishes pretty quickly, which makes it seem like something internal is waiting for a fixed or random amount of time rather than until the editor is finished building.
The bug is not present in the builtin ui toolkit, only if you install the preview through the package manager (com.unity.ui 1.0.0-preview.14 for us), so it’s a regression.
I’ve tried whatever I can think of to work around the bug, but I haven’t actually figured out anything that works. Any advice?
As a workaround, I think you could create your property field with the empty constructor and put your binding at the end of the method, using BindProperty().
var objectField = new PropertyField();
objectField.label = "Material";
objectField.RegisterValueChangeCallback(evt => Debug.Log("The event was invoked!"));
root.Add(objectField);
Thread.Sleep(5000);
objectField.BindProperty(serializedObject.FindProperty("material"));
The event gets invoked twice if I do that. Once because of the bug, and once because it seems like BindProperty causes that to happen regardless:
This has the event get invoked once with no Wait:
var objectField = new PropertyField();
objectField.label = "Material";
root.Add(objectField);
objectField.RegisterValueChangeCallback(evt => Debug.Log("The event was invoked!"));
objectField.BindProperty(serializedObject.FindProperty("material"));
This also has the event get invoked once with no Wait:
var objectField = new PropertyField();
objectField.label = "Material";
root.Add(objectField);
objectField.BindProperty(serializedObject.FindProperty("material"));
objectField.RegisterValueChangeCallback(evt => Debug.Log("The event was invoked!"));
So the usual strategy of first setting the value and then assigning the callback seems to just not work. It seems like BindProperty() queues the serializedProperty/display sync for some later time instead of doing it right away.