Drag and Drop Issue 1314

Hi,
After updating to version 1314 the drag and drop functionality I’ve implement change in a way that now when I drag a node from the palette to the diagram the mouse always turn into “no enter” sign and I can’t drop the node.

Can you please tell me if there any change in the logic of the drag and drop functionality?

Thank you,
Ido.

In diff’ing the code, I find hardly any changes in drag-and-drop functionality for WPF. The primary change, as documented in the release notes, is the addition of two new virtual methods that make it easier to customize: DraggingTool.MayAcceptData and AcceptData. But I believe that the default implementations of those methods are exactly the same code that used to be inside DraggingTool’s other methods.

And drag-and-drop from a Palette to a Diagram isn’t fundamentally broken, as you can see when running several of the sample applications that depend on it.

How have you customized your own application’s drag-and-drop functionality?

Yes, I’ve create custom DraggingTool. I override MayCopyExternal and DoDrop only

Well, DoDrop doesn’t matter here, because you aren’t getting to that code before having the problem, right?

Could you step through your MayCopyExternal code to see if it’s behaving correctly?

You are right.
MayCopyExternal return true.

The base implementation of AcceptData return null.

Ido.

If MayCopyExternal is returning true, DoDragEnter and DoDragOver will both set DragEventArgs.Effects to DragDropEffects.Copy. So I don’t know why you are getting a “no-drop-allowed” cursor.

FYI, MayCopyExternal is one of the methods that was changed to call the new MayAcceptData predicate. But if you have replaced the definition by your override without calling the base method, it’s not calling the new predicate anyway.

However, I’m curious if AcceptData is returning a non-null object. Could you override it to see what it returns? It ought to be called by DoDragOver when MayCopyExternal returns true.

AcceptData is getting called when I’m overriding it and when I call base.AcceptData it return null.
Since you are now obfuscating your code in a way that is really unreadable (you’r doing wonderful work with it) I can’t see what AcceptData does, all I can see is that it is using the DiagramModel.DataFormat of the diagram that the dragging tool belongs to which is my main diagram (not the palette).
The reason I’m bringing this up is because my palette’s DataFormat is different from the main diagram DataFormat. I actually have two different palettes and each of the palette’s model have different DataFormat so it will be easy to know from which palette the user drag the data.

Even when I return the DataCollection from my override AcceptData the drop sign did not change to copy.

Thank you,
Ido.

Here’s our implementation:

public virtual bool MayAcceptData(IDataObject dataobj) { Diagram diagram = this.Diagram; if (diagram == null) return false; IDiagramModel model = diagram.Model; if (model == null) return false; return dataobj.GetDataPresent(model.DataFormat) || dataobj.GetDataPresent(model.GetNodeType()); }

public virtual IDataCollection AcceptData(IDataObject dataobj) { Diagram diagram = this.Diagram; if (diagram == null) return null; IDiagramModel model = diagram.Model; if (model == null) return null; IDataCollection data = dataobj.GetData(model.DataFormat) as IDataCollection; if (data == null) { // also handle single node data, instead of an IDataCollection Object nodedata = dataobj.GetData(model.GetNodeType()); if (nodedata != null) { data = model.CreateDataCollection(); data.AddNode(nodedata); } } return data; }
As far as I can tell, the code is equivalent to what was in version 1.2.
In both cases it is using this.Diagram.Model.DataFormat of the dragging tool.

You have the code now, so you can adapt it to your needs.
But I’m still interested in an explanation for how 1.3 is incompatible with 1.2.

I’ll run version 1.2 and see which code path get me into OnDrag.
Thank you very much.

Hi Walter,
I’ve narrow down the problem and found that the root cause is a NullReferenceExcpetion thrown from DraggingTool obfuscated method name #vw with this stack trace I’ve caught by creating custom DiagramPanel and override the DoDragOver method:


   at Northwoods.GoXam.Tool.DraggingTool.#vw(IDataObject dataobj, Boolean undoable)
   at Northwoods.GoXam.Tool.DraggingTool.DoDragOver(DragEventArgs e)
   at Northwoods.GoXam.DiagramPanel.OnDragOver(DragEventArgs e)
   at Org.Dna.Aurora.Presentation.Standard.NetworkViews.GoXam.DEBUGDiagramPanel.OnDragOver(DragEventArgs e) 

I’ve caught that exception which got the DoDrop method to be called but it’s then cause the following exception:


DispatcherUnhandledExcpetion: System.InvalidCastException: Unable to cast object of type 'Org.Dna.Aurora.UIFramework.Repositories.RepositoryItemView' to type 'Org.Dna.Aurora.Presentation.Standard.NetworkViews.GoXam.VisualNodeViewModel'.
   at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
   at System.Collections.Generic.HashSet`1.UnionWith(IEnumerable`1 other)
   at System.Collections.Generic.HashSet`1..ctor(IEnumerable`1 collection, IEqualityComparer`1 comparer)
   at Northwoods.GoXam.Model.GraphLinksModel`4.DataCollection.set_Nodes(IEnumerable`1 value)
   at Northwoods.GoXam.Model.GraphLinksModel`4.DataCollection.#Bo(IEnumerable`1 value)
   at Northwoods.GoXam.PartManager.CopyParts(IEnumerable`1 coll, IDiagramModel destmodel)
   at Northwoods.GoXam.Tool.DraggingTool.#8v(IEnumerable`1 originals, Boolean undoable)
   at Northwoods.GoXam.Tool.DraggingTool.#tw()
   at Northwoods.GoXam.DiagramPanel.#nr(MouseEventArgs e, Diagram other, Point modelpt)
   at Northwoods.GoXam.DiagramPanel.OnMouseMove(MouseEventArgs e)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)

As you can see it fail to cast an instance from type RepositoryItemView to type VisualNodeViewModel.
RepositoryItemView is the type of node I have for the palette model and VisualNodeViewModel is the type of node of the main diagram’s model.

In the previous version when I drag a node from the palette to the main diagram I did not had the ghost node following the mouse, maybe it’s a sign that I did something wrong in the previous version as well but it was more forgiving then the current version.

Is there a requirement that both the palette and the main diagram’s model will be the same?

Thank you,
Ido.

I don’t think that the types of the diagram models have to be exactly the same, but by default they should be. Certainly it doesn’t make sense for the node data types to be incompatible.

How do you suggest to debug what happen inside DoDrag and OnMouseMove?

I’ve reproduce the problem of dragging from palette with model that has DataFormat different from the main diagram’s model DataFormat, and even though I’m overriding MayCopyExternal and return true the mouse cursor does not change to copy sign.

It is causing the NullReferenceException with the minimal code I can think of.

I’ve repro it using your sample code, specifically the FlowChart sample.
The code is in gist at the address https://gist.github.com/1616572

You just need to copy it into the FlowChart.xaml.cs and it will work (or not work :)

Thank you very very much for the help.

Ido.

Does RepositoryViewModel inherit from VisualNodeViewModel? I believe you are saying that it doesn’t.

If it doesn’t, how could you expect it to work? How did you define the models for the Palette and for the target Diagram?

You are right, RepositoryItemView does not inherit from VisualNodeViewModel.
The reason I expect it to work because I handle the drop of the item, I don’t expect GoWPF to handle the drop because I use incompatible types.

I’ve override AcceptData and return the IDataCollection instance exist in the IDataObject parameter, but then I get an InvalidCastException because GoWPF try to convert the data collection of RepositoryView to data collection of VisualNodeViewModel.


System.InvalidCastException: Unable to cast object of type 'DataCollection[Org.Dna.Aurora.UIFramework.Repositories.RepositoryItemView,System.String,System.String,System.Int32]' to type 'DataCollection[Org.Dna.Aurora.Presentation.Standard.NetworkViews.GoXam.VisualNodeViewModel,Org.Dna.Aurora.Presentation.Standard.NetworkViews.VisualNodeKey,System.String,Org.Dna.Aurora.Presentation.Standard.NetworkViews.GoXam.VisualLinkViewModel]'.
   at Northwoods.GoXam.Model.GraphLinksModel`4.#hn(IDataCollection coll, ICopyDictionary env)
   at Northwoods.GoXam.Tool.DraggingTool.#vw(IDataObject dataobj, Boolean undoable)
   at Northwoods.GoXam.Tool.DraggingTool.DoDragOver(DragEventArgs e)
   at Northwoods.GoXam.DiagramPanel.OnDragOver(DragEventArgs e)
   at Org.Dna.Aurora.Presentation.Standard.NetworkViews.GoXam.DEBUGDiagramPanel.OnDragOver(DragEventArgs e) in Org.Dna.Aurora.Presentation.Standard\NetworkViews\GoXam\DEBUGDiagramPanel.cs

The reason I have different diagram models is because the palette show things like object type (employee, role, machine) while the main diagram show instances of them.

I’ll try to make the palette model type compatible with the main diagram model types.

Ido.

It seems to me that you don’t need to change the different model of the Palette to be the same of the target Diagram, if you override MayAcceptData and AcceptData to handle converting the data from the Palette’s model to the Diagram’s model.

Hello Walter,
I was able to get the drag and drop working by using the same diagram model for both the palette and the main diagram.

It is still the case that a code that worked on version 1.2 is not working on version 1.3. I understand I did something that was not expected and is not meant to be supported but it is still the case.
The reason I had completely different diagram model for the palette and the main diagram was that I am handling all the drop procedure and I’m not expecting GoWPF to actually add new node to the main diagram’s model.

Another issue that still exist is that I now want to share the DataTemplates of the draggable nodes with both the palette and the main diagram. Opposed to the sample applications you have I don’t want to have one big DataTemplateDictionary, I rather take WPF approach of having multiple manageable DataTemplateDictionary and be able to merge them together as needed. In this specific case I will have “DraggableDataTemplates” and “StandardDataTemplate”, the palette will have only the DraggableDataTemplates and the main diagram will have both the StandardDataTemplates and the DraggableDataTemplates.

Currently I hold one big dictionary which can quickly become unmanageable.

Thank you again for the great support,
Ido.

Just because the models are the same (even though I still believe they don’t have to be if you override AcceptData appropriately) doesn’t mean the data templates have to be the same.

If I were you I would still have two separate DataTemplateDictionarys. But I don’t know of any way of “merging” dictionaries in XAML – what there is is specific to resource dictionaries. So I would declare a third DataTemplateDictionary that is used by your main diagram and merge the two dictionaries into the third one by copying their items in code.

BTW, I think this new issue should have been a separate forum topic – it would make it easier to find and understand for other people interested in this dictionaries but not in the drag-and-drop issue that this topic has mostly discussed.

Hi,
I’ll create a separate topic for the merged dictionaries.

  1. Having different models and implementing AcceptData does work as you explain. In my case it made more sense to change the palette model because I control both the main and the palette model.

  2. I understand I can have different templates for the same model, I already use it in the main diagram and the overview diagram which both connected to the exact same model.

This issue is resolved as far as I concerned. It was a mistake I made in the previous version and it has now been fixed.

Thank you,
Ido.