Bug with Undo functionality while binding.

TLDR Version- I want to know whats the best way to display Propertyfield’s of SerializedFields of an EditorWindow class without creating ghost Undo’s that are still present once the EditorWindow is deleted.

Detailed Version:
The way I first tried to display fields for a serialized variable of the editor window object was to use a Property Field bound(or binded?? I dont know lol) to the variable because EditorWindows inherit from Scriptable Object and everything seemed to work fine. I make changes in the Editor Window and it updates the variables and vice versa, and Undo works just as expected too.

But the problem starts to show up once the Editor Window is deleted. An Undo is registered when I change a property field, and the undo persists even after closing the Editor Window. So if I open an Editor Window and make like around 20 changes and then close the window, now if I want to revert the editor back to how it was before opening the Editor Window, I would have to Undo 20 times for no reason, because the Editor Window doesnt reopen too when you Undo.

The Undo’s name is “Undo Inspector”, so basically I would have 20 blank ‘Undo Inspector’ undos because the Editor Window is no longer open.

So can some tell me a better way to do this? Removing the Undos for an object once the concerned object is deleted should be a built in thing unless explicitly told to not happen with Undo.DeleteObject or whatever that function is cant remember it correctly atm.

Steps to recreate this issue:

Step 1: Opening the Editor Window for the first time

Step 2: Changing a value

Step 3: Checking out the undo created for the change


Step 4: Doing the change for like 10 times(Repeat of Step 2)

Step 5: Closing the Window

Step 6: Check that the undo still persists as a ghost Undo(not one but ten of these because we changed value like ten times), but we dont want that!!


My Editor Window Script:

using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;

public class TestEditorWindow : EditorWindow
{
    #region Variables
    private static TestEditorWindow s_Window;
    [SerializeField] private float m_TestFloat;
    #endregion

    [MenuItem("Test Editor Window/Open")]
    public static void OpenWindow_F()
    {
        if (s_Window != null) s_Window.Close();
        s_Window = CreateWindow<TestEditorWindow>();
    }

    private void OnEnable()
    {
        UICreate_F();
    }

    private void UICreate_F()
    {
        PropertyField propertyField0 = new PropertyField();
        propertyField0.BindProperty(new SerializedObject(this).FindProperty("m_TestFloat"));
        rootVisualElement.Add(propertyField0);
    }
}

There’s another… i guess its a bug im not really sure but creating a property field and binding it to a serializedProperty through the constructor parameters dont work, the property field is not displayed at all.

PropertyField propertyField0 = new PropertyField(new SerializedObject(this).FindProperty("m_TestFloat"));

rootVisualElement.Add(propertyField0);

But it works if I bind it using the BindProperty method of the PropertyField.

PropertyField propertyField0 = new PropertyField();
propertyField0.BindProperty(new SerializedObject(this).FindProperty("m_TestFloat"));
rootVisualElement.Add(propertyField0);

Nothing major but a one liner becomes a three liner because of this :smile:.

Unfortunately the current Unity API doesn’t allow you do discard undos on object after the object is deleted.

You can observe this behavior with the following repro steps:
1- Create material
2- Select the material
3- Do a bunch of edit in the Inspector
4- Delete the material.
5- See that a bunch of undos still exist and relates to the deleted material.

There are no public API to crawl the undo queue and remove specific command.

As for the Binding system in the PropertyField: you are doing the right thing. If you used a UXML and use the loadTree function I think the binding is done automatically.

We partially solved this in the UI Builder (which also wants to have it’s own “undo stack” bound to its lifetime) using:

to register our own Undo actions on a separate self-managed ScriptableObject (not the EditorWindow itself). Then use:

on Window close to clear all undos related to our state object. This does also mean you have to do you own custom bindings where you modify the object directly and not via PropertyFields and SerializedObject bindings.

Though, it’s worth pushing the limits I listed above since it might work in more cases than what I tested.

Sorry for the late reply, but yeah, I was using Undo.RegiserCompleteObjectUndo like u said before, but it would have been better if it happened automatically and I felt it wasnt worth it to do it like that. But if Undo works that way, then I guess i’ll just have to deal with it.

Thanks you for clearing my doubts!