InsertNode called twice

I have a GoPalette and GoDiagram and want to allow users to drag items from the Palette onto the diagram. Each node in the palette may be dragged repeatedly, so I need the nodes to be unique in the diagram. I’m also using a custom dragging tool to highlight and validate the target node before allowing nodes to be dropped.

When the drag starts, a node is created immediately within the model, and then when the node is dropped, another one is created. The first node is inserted without any problem. The second node of the same type throws an exception.

I have tried using strings, ints and Guids for the key type. I have also tried overriding the MakeNodeKeyUnique method as well as overriding the InsertNode methods. Nothing seems to work.
My custom GraphModel is defined as follows:

    public class MyElement : GraphModelNodeData<Guid> 
    { 
        public MyElement() 
        { 
            Key = Guid.NewGuid(); 
        } 
        
        public Guid ID 
        { 
            get { return Key; } 
            set { Key = value; } 
        } 
    }   
public class MyModel : GraphModel<MyElement, Guid>
{

    <span style="color: blue;">public</span> MyModel(<span style="color: blue;">object</span> owner)
    {
        NodeKeyPath = <span style="color: rgb163, 21, 21;">"ID"</span>;
        Modifiable = <span style="color: blue;">true</span>;
        HasUndoManager = <span style="color: blue;">true</span>;
    }

protected override void InsertNode(MyElement nodedata)
{
Debug.WriteLine(“MyModel.InsertNode({0})”, nodedata.Key);
Guid key = FindKeyForNode(nodedata);
MyElement e = FindNodeByKey(key);
if (e != null)
{
Debug.WriteLine(string.Format(“Found duplicate key: {0}”, key));
nodedata.ID = Guid.NewGuid();
Debug.WriteLine(string.Format("==> creating new key: {0}", nodedata.Key));
}
        <span style="color: blue;">try</span>
        {
            <span style="color: blue;">base</span>.InsertNode(nodedata);
        }
        <span style="color: blue;">catch</span> (<span style="color: rgb43, 145, 175;">Exception</span> x)
        {
            <span style="color: rgb43, 145, 175;">Debug</span>.WriteLine(<span style="color: blue;">string</span>.Format(<span style="color: rgb163, 21, 21;">"Exception: {0}"</span>, x.Message));
            <span style="color: rgb43, 145, 175;">Debug</span>.WriteLine(<span style="color: rgb163, 21, 21;">"Attempting to clone the node..."</span>);
            <span style="color: blue;">base</span>.InsertNode(nodedata.Clone() <span style="color: blue;">as</span> <span style="color: rgb43, 145, 175;">MyElement</span>);
        }
    }

protected override bool MakeNodeKeyUnique(MyElement nodedata)
{
Debug.WriteLine(“MyModel.MakeNodeKeyUnique({0})”, nodedata.Key);
nodedata.ID = Guid.NewGuid();
Debug.WriteLine("==> {0}", nodedata.Key);
return true;
}
}
 

That’s a bug in MakeNodeKeyUnique. Sorry about that.

We have fixed this in 1.3.4. You won’t need those overrides of InsertNode or MakeNodeKeyUnique.

You do not need that NewGuid() in the MyElement constructor either, even in 1.3.3.

Thank you very much, but I think I’m still taking the wrong approach. Conceptually, I need to insert a lot of nodes when an item is dropped from a palette. Is there a better example of using ExternalObjectsDropped? Also, when using that approach in conjunction with a custom dragging tool, is it necessary to suppress the invocation of the base.DropOnto method? I need to provide visual feedback to indicate a valid drop target.

It is common for the Diagram.ExternalObjectsDropped event handler to modify the Diagram.SelectedParts that were dropped, either directly or through the bound data. The Planogram sample demonstrates that.

But it’s certainly normal to add a bunch of nodes and links in the area where the drop occurred, perhaps even deleting the actual dropped parts.

The DraggingTool.DropOnto method is what normally implements various standard behaviors for additional actions besides just moving or copying parts.

The DraggingTool.DragOver method is what normally implements additional actions during the dragging operation to affect stationary parts. Frequently the visual feedback is implemented via a data-binding on the go:Part.IsDropOntoAccepted attached dependency property. There are several examples of this in the demo.

Thanks for the tips, but this is not working the way I understood it should. I’m using the same model (derived from TreeModel) and node data (derived from TreeModelNodeData) classes for the palette as well as for the diagram. When the user drags an item from the palette and drops it onto the canvas, I want to discard the node that was dropped, and then insert a bunch of new nodes at that spot, optionally connecting them to the target node if they dropped it on top of one.

In my custom dragging tool, I detect whether they are dropping onto the background or onto another node. In both cases, I want to create new nodes and add them to the canvas. However, this is where it gets murky.

My custom dragging tool handles the DragOver and DropOnto methods. In the DragOver method, it checks the node being dragged against the node being targeted, and allows or disallows the drop while highlighting the target node if it is a valid target. This works fine.

In the DropOnto method, it extracts the node data, which is an instance of a separate class I’m associating with each node, but only in the palette. Using this node data as a descriptor, I want to create the appropriate nodes in the diagram, resulting in a tree of nodes linked to the target node.

Should I do this INSTEAD of calling base.DropOnto(pt) or should I go ahead and delegate to the base class method, allowing it to insert the node and THEN create my additional nodes in the Diagram.ExternalObjectsDropped override? This feels wrong, since as you mentioned, DraggingTool.DropOnto does a lot more than just moving and copying parts. I believe I should allow it to do whatever it does, and then patch the diagram afterwards. But at this point, I’m pretty confused as to the proper course of action.

On the other hand, if I’m supposed to call the base class and patch the diagram after the drop, then how exactly is this done? Stepping through the debugger, Diagram.ExternalObjectsDropped is called immediately after CustomDraggingTool.DropOnto, but the SelectedParts collection is always empty. Unfortunately, the Planogram sample you mentioned does not really apply here because it simply adjusts the sizes of nodes that have already been added to the diagram and assumes that there are always parts selected in the diagram. In my case, there will either be nothing in the diagram, or the user will have dropped a node onto an existing node. I can find no other samples that demonstrate how to use Diagram.ExternalObjectsDropped.

The DraggingTool.DropOnto method may perform side-effects in addition to the basic drop action of inserting a node (or several nodes) at the given point and selecting it (or them).

Most of what that method does requires DropOntoEnabled to be true and some Part’s DropOntoBehavior to be something. There are some other side-effects which might not apply to your situation, such as making sure that any AvoidsNodes links are re-routed if they happen to go through the area where the new nodes are.

So you might or might not want to call the base method, but it depends on what your application needs.

In either case the new nodes have already been dropped into the diagram and selected, so I do not understand how the selection could be empty, unless you have already removed them.

So, is it possible hook into DraggingTool.DropOnto to specify a different set of nodes to be inserted?

To recap:

  1. The nodes in the palette are DIFFERENT than the nodes I want to create in the diagram. The palette essentially contains instructions that I need to interpret in order to add the appropriate nodes to the diagram.

  2. By default, the DraggingTool automatically clones the incoming node in order to perform its dragging behavior, but this is not ideal. I would rather have it wait until the user drops the node and THEN take over the creation of the new nodes. Is there an example of this somewhere?

  3. If I cannot prevent the automatic insertion of nodes, then (according to your response) deleting the node that is inserted by DraggingTool.DropOnto may have undesirable side-effects?

Here is a modification of the LogicCircuit sample that deletes the dropped node that comes from a different diagram and inserts two copies of the node connected by a link (if possible). public class CustomDraggingTool : DraggingTool { protected override void DropOnto(Point pt) { base.DropOnto(pt); if (this.Active) return; // no-op if it's an internal drag-and-drop // [from here on could be implemented in a Diagram.ExternalObjectsDropped event handler // instead of in a DropOnto method override] // find a Node that was dropped (ignores other nodes) var n = this.Diagram.SelectedParts.OfType<Node>().FirstOrDefault(); if (n == null) return; var d = n.Data as GateData; if (d == null) return; // remember the dropped node(data)'s location var loc = d.Location; // delete that new node(data) this.Diagram.Model.RemoveNode(d); // create two new copies of the node(data) var d2 = this.Diagram.Model.AddNodeCopy(d) as GateData; if (d2 != null) d2.Location = new Point(loc.X-50, loc.Y); var d3 = this.Diagram.Model.AddNodeCopy(d) as GateData; if (d3 != null) d3.Location = new Point(loc.X+50, loc.Y); // if can link the two nodes together, do so if (d2 != null && d2.GateType != "Output" && d3 != null && d3.GateType != "Input") { string inputport = ""; if (d3.GateType == "OneInOneOut") inputport = "In"; else if (d3.GateType == "TwoInOneOut") inputport = "In2"; this.Diagram.Model.AddLink(d2, null, d3, inputport); } } }
Install this tool on your Diagram either in XAML or by code.

Note that basically all of that code would normally be implemented in a Diagram.ExternalObjectsDropped event handler, since that event only happens for external drops. But since you want to customize the dragging tool, I put the code in there.

Thank you very much. This was what I needed to see.