How to save ScriptableObject when data is changed in (bound) visual elements?

I have created a custom editor for a ScriptableObject, which changes data in a list. The list draws a set of property fields for each element (structs) with a custom property drawer. The custom property drawer simply draws object fields for the data that the user can change:

public class MyAsset : ScriptableObject
{
    [System.Serializable]
    public struct MyStruct
    {
        public ulong id;
        public Sprite sprite;
    }

    [SerializeField] private List<MyStruct> entries = new List<MyStruct>();
}
var list = new ListView()
{
    bindingPath = "entries",
    // ...etc...
};
var field = new ObjectField() { objectType = typeof(Sprite) };
field.BindProperty(property.FindPropertyRelative("sprite"));

Everything works totally fine, except that none of the data changed is actually reflected in the ScriptableAsset object stored on disk. My understanding is that binding the properties also takes care of setting any flags needed for saving.

This apparently isn’t the case, so what is going on? I found this thread ( Saving ScriptableObject fields from a Custom Editor and using UIElements ) in which @uDamian mentions hooking up a callback for a ChangeEvent on the root VisualElement, but this doesn’t seem to work. With the following code, my callback simply never gets called:

VisualElement root = new VisualElement();

root.RegisterCallback<ChangeEvent<object>>(evt =>
{
    Debug.Log($"Event: {evt} {evt.target} {evt.propagationPhase}");
});

var list = new ListView() {/* list content*/};

root.Add(list);
return root;

What is the supposed workflow here?

I think it should be ChangeEvent, not ChangeEvent. You can also try (as recommended) using a PropertyField instead of an ObjectField. Then you can listen for the SerializedPropertyChangeEvent event, since you don’t care about the new value, just when it has changed.