Object created via drag&drop is deleted immediately when using RegisterCreatedObjectUndo

Hi,
I am trying to create a script, that allows the user to add an image to the scene via drag&drop (similar to dragging a prefab into the hierarchy). I used this post as a starting point and the script worked, but the new object in the scene was not registered as a change and therefore not saved.

I tried to use Undo.RegisterCreatedObjectUndo, but if I do that, the object appears for a split second and then gets deleted. This only happens when the function for creating the image object is called from EditorApplication.hierarchyWindowItemOnGUI. I tested calling the function from a menu entry and loading the image from the AssetDatabase and that works as intended. So I guess the problem stems from the way I process the drag&drop or something happens in/after EditorApplication.hierarchyWindowItemOnGUI.

You can see the behaviour here (link to GIF). It looks like this (link to GIF) if I remove RegisterCreatedObjectUndo (notice how the item is deselected immediately) and like this (link to GIF) when I call the function from a menu entry.

Thanks

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[InitializeOnLoad]
public class DragImage : Editor
{

    static DragImage()
    {
        EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowCallback;       
    }

    static void HierarchyWindowCallback(int id, Rect pos)
    {
        if (Event.current.type == EventType.DragExited)
        {
            if(DragDropCallback()) {
                Event.current.Use();
            }
        }
    }   

    static bool DragDropCallback()
    {
        DragAndDrop.AcceptDrag();
        List<Texture2D> images = new List<Texture2D>();
        foreach (Object obj in DragAndDrop.objectReferences)
        {
            if (obj.GetType() == typeof(Texture2D))
            {
                Texture2D tex = (Texture2D)obj;
                images.Add(tex);
            }
        }

        foreach(Texture2D img in images)
        {
            CreateImage(img);
        }
        return images.Count>0;
    }

    [MenuItem("GameObject/3D Object/TestImage")]
    static void CreateInMenu()
    {
        CreateImage(AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/test.png"));
    }


    static void CreateImage(Texture2D tex)
    {
        GameObject imgObject = GameObject.CreatePrimitive(PrimitiveType.Plane);
        imgObject.name = "Image";
        imgObject.transform.localScale = new Vector3(0.1f * ((float)tex.width) / ((float)tex.height), 1f, 0.1f);
        imgObject.transform.rotation = Quaternion.Euler(90f, 0f, 180f);
        imgObject.transform.position = new Vector3(0f, 1.5f, 0f);
        MeshRenderer renderer = imgObject.GetComponent<MeshRenderer>();
        Material mat = new Material(Shader.Find("Standard"));
        mat.SetTexture("_MainTex", tex);
        mat.SetColor("_Color", Color.grey);
        mat.SetFloat("_Glossiness", 0.0f);
        renderer.material = mat;
        Undo.RegisterCreatedObjectUndo(imgObject, "Create image");
        Selection.activeObject = imgObject;
    }
}

The following code should work as expected. However, since it’s a callback for “items in the hierarchy window”, it only accepts the drop if the mouse is located on an item.

static void HierarchyWindowCallback(int id, Rect pos)
{
    if (!pos.Contains(Event.current.mousePosition))
        return;

    var eventType = Event.current.type;
    if (eventType == EventType.DragUpdated || eventType == EventType.DragPerform)
    {
        DragAndDrop.visualMode = DragAndDropVisualMode.Copy;

        if (eventType == EventType.DragPerform)
        {
            DragAndDrop.AcceptDrag();
            DragDropCallback();
        }

        Event.current.Use();
    }
}

Thanks for your answer. I did not realize, that the DragPerform event is only triggered, when you drag onto an existing item. Now it works when I drag onto an existing item, but that’s not very practical, so I switched to SceneView.onSceneGUIDelegate (to drag&drop into the scene view).

My final code looks like this:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[InitializeOnLoad]
public class DragImage : Editor
{

    static DragImage()
    {
        SceneView.onSceneGUIDelegate += SceneViewCallback;
    }

    static void SceneViewCallback(SceneView view)
    {
        if (Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform)
        {
            DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
        }
        if (Event.current.type == EventType.DragPerform)
        {
            DragAndDrop.AcceptDrag();
            if (DragDropCallback())
            {
                Event.current.Use();
            }
        }
    }
       

    static bool DragDropCallback()
    {       
        List<Texture2D> images = new List<Texture2D>();
        foreach (Object obj in DragAndDrop.objectReferences)
        {
            if (obj.GetType() == typeof(Texture2D))
            {
                Texture2D tex = (Texture2D)obj;
                images.Add(tex);
            }
        }

        foreach(Texture2D img in images)
        {
            CreateImage(img);
        }
        return images.Count>0;
    }

    static void CreateImage(Texture2D tex)
    {
        GameObject imgObject = GameObject.CreatePrimitive(PrimitiveType.Plane);
        imgObject.name = "Image";
        imgObject.transform.localScale = new Vector3(0.1f * ((float)tex.width) / ((float)tex.height), 1f, 0.1f);
        imgObject.transform.rotation = Quaternion.Euler(90f, 0f, 180f);
        imgObject.transform.position = new Vector3(0f, 1.5f, 0f);
        MeshRenderer renderer = imgObject.GetComponent<MeshRenderer>();
        Material mat = new Material(Shader.Find("Standard"));
        mat.SetTexture("_MainTex", tex);
        mat.SetColor("_Color", Color.grey);
        mat.SetFloat("_Glossiness", 0.0f);
        renderer.material = mat;
        Undo.RegisterCreatedObjectUndo(imgObject, "Create image");
        Selection.activeObject = imgObject;
    }
}