GoXam External Drag and Drop

I am new to GoXam, so bare with me ;-)

I have a Telerik Tree right next to my GoDiagram, and I want to be able to drag in tree elements to create new nodes in my diagram.
I thought I could just register for the ExternalObjectsDropped event like this:
this.organizationDiagram.ExternalObjectsDropped += new EventHandler<Northwoods.GoXam.DiagramEventArgs>(organizationDiagram_ExternalObjectsDropped);
But I am not getting the event at all...
Could somebody tell me what the exact steps are that I have to perform to make something like this work.
Thanks a lot for the help

Are you using Silverlight or WPF?

If you are using WPF:

The easiest implementation strategy is to make sure that the drag-and-drop data is an instance of your model’s node data type.

For example, consider the case where my Diagram.Model was created as an instance of GraphLinksModel<TestData, Object, String, TestLink>. TestData and TestLink are data classes that I have defined. And make sure you have set DiagramModel.Modifiable to true.

Then you could build a TreeView where each TreeViewItem had an instance of TestData associated with it. For this example, I have built the tree in XAML and the TreeViewItem.Header is a TestData.

  <TreeView x:Name="myTreeView" SelectedValuePath="Header" MouseMove="myTreeView_MouseMove">
    <TreeViewItem Header="Root">
      <TreeViewItem>
        <TreeViewItem.Header>
          <local:TestData Name="First Data" />
        </TreeViewItem.Header>
      </TreeViewItem>
      <TreeViewItem>
        <TreeViewItem.Header>
          <local:TestData Name="Second Data" />
        </TreeViewItem.Header>
      </TreeViewItem>
    </TreeViewItem>
  </TreeView>

(Assume TestData.ToString() returns its Name property, or something like that.)

The drag-and-drop could be started via:

private void myTreeView_MouseMove(object sender, MouseEventArgs e) {
  if (e.LeftButton == MouseButtonState.Pressed && myTreeView.SelectedItem != null) {
    DragDrop.DoDragDrop(myTreeView, myTreeView.SelectedValue, DragDropEffects.Copy);
  }
}

Note that this code depends on TreeView.SelectedValuePath=“Header”, as an easy way of getting the TreeViewItem's header data.

If you also make sure that Diagram.AllowDrop=“True”, I think you’ll find that everything just works.

There’s another example using a ListBox and using multiple data classes in this post.

If you are using Silverlight:

Silverlight doesn’t support drag-and-drop, so there isn’t any built-in support for handling drops from other elements except when copying from another Diagram. (Well, Silverlight 4 supports a very restricted form of drag-and-drop, but that doesn’t help either of us.)

I’m not familiar with Telerik’s drag-and-drop mechanism in Silverlight. You’ll need to implement your own drop handler, because Diagram.ExternalObjectsDropped won’t be involved.

There is “common” drag-and-drop support in the Silverlight Toolkit from silverlight.codeplex.com. I know that that works with GoXam, from TreeViews to Diagram. But the caution is that this functionality is considered “Experimental” by Microsoft.

OK, I have tried the Silverlight toolkit’s drag-and-drop support for TreeViews. I purposely used the older Silverlight 3 toolkit from November 2009, to make sure this stuff works in Silverlight 3 and (presumably) also with the toolkit for Silverlight 4.

Here are the XMLNS declarations, which work in Silverlight 3 or 4:

xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" xmlns:mswindows="clr-namespace:Microsoft.Windows;assembly=System.Windows.Controls.Toolkit"
Note the use of two DLLs, System.Windows.Controls.DLL for TreeView and System.Windows.Controls.Toolkit.DLL for drag-and-drop support.

Here’s the TreeView:

<toolkit:TreeViewDragDropTarget Grid.Column="0" mswindows:DragDrop.AllowDrop="False" AllowedSourceEffects="Copy" ItemDroppedOnTarget="TreeViewDragDropTarget_ItemDroppedOnTarget"> <controls:TreeView x:Name="myTreeView" SelectedValuePath="Header"> <controls:TreeViewItem Header="Root"> <controls:TreeViewItem> <controls:TreeViewItem.Header> <local:TestData Key="First Data" /> </controls:TreeViewItem.Header> </controls:TreeViewItem> <controls:TreeViewItem> <controls:TreeViewItem.Header> <local:TestData Key="Second Data" /> </controls:TreeViewItem.Header> </controls:TreeViewItem> </controls:TreeViewItem> </controls:TreeView> </toolkit:TreeViewDragDropTarget>
The Diagram is declared like:

<go:Diagram x:Name="myDiagram" Grid.Column="1" mswindows:DragDrop.AllowDrop="True" . . . />
And the ItemDroppedOnTarget event handler:

private void TreeViewDragDropTarget_ItemDroppedOnTarget(object sender, ItemDragEventArgs e) { var coll = e.Data as SelectionCollection; if (coll != null) { myDiagram.StartTransaction("Dropped data"); foreach (Selection sel in coll) { var item = sel.Item as TreeViewItem; if (item != null) { var data = item.Header as TestData; if (data != null) { var newdata = myDiagram.Model.AddNodeCopy(data) as TestData; if (newdata != null) newdata.Location = myDiagram.LastMousePointInModel; } } } myDiagram.CommitTransaction("Dropped data"); } }
Of course you’ll need to customize the code for your own data.

Walter,
Thanks for this post.
Please see how one can implement dropping data on nodes from ListBox. The example shows how to drop some object associated to a ListBox item and onto another object associated to the GoXam node. I use toolkit for SL4. The flow is simple.
1. Use toolkit to enable draging from listbox.
2. Drop event is handled from listbox drop event, how ever it is unaware of the element that it's dropped upon so.
3. Drop target is managed via mouse_enter,mouse_leave events.
XAML
<UserControl.Resources>
<DataTemplate x:Key="yourResouceKey">
<toolkit:ListBoxDragDropTarget ItemDroppedOnTarget="ListBoxDragDropTarget_ItemDroppedOnTarget">
<ListBox
ItemsSource="{Binding Mode=OneWay}"
Style="{StaticResource AccordionListBoxStyle}"
ItemContainerStyle="{StaticResource DraggableItemStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name, Mode=OneWay}" Margin="0,3" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</toolkit:ListBoxDragDropTarget>
 </DataTemplate>
C#
Drop Event:

private void ListBoxDragDropTarget_ItemDroppedOnTarget(object sender, System.Windows.Controls.ItemDragEventArgs e)
{
SelectionCollection selectionCollection = e.Data as SelectionCollection;
Selection sel = selectionCollection[0];
if (sel != null && Globals.Instance.ActiveSpotPanel != null)
{
e.Handled = ProcessDrop(Globals.Instance.ActiveSpotPanel, Globals.Instance.ActiveNode, sel.Item);
}
else
{

e.Handled = true;
}
}
 
Event Handling and process:
private bool ProcessDrop(Northwoods.GoXam.SpotPanel sp,Northwoods.GoXam.Node node, object Data)
{
bool dropMatch = false;
bool handled = false;
MyNodeObjectnodeData = (MyNodeObject)node.Data as MyNodeObject;
if (Data is Document)
{
Document payload = Data as Document;
dropMatch = SetDocumentNode(ref nodeData, payload);
if (!dropMatch) { ShowDropError(nodeData.Type, "document"); }
handled = true;
}
	   return handled;
	}
 
MyNodeObject is a class that is associated as the Data Context of each node
 
These are the events that are used to store the drop target in a static Globals class.
private void Node_MouseEnter(object sender, MouseEventArgs e)
{
if (Globals.Instance.DiagramMode == DiagramMode.Design)
{
SetTagVisible(sender as UIElement, true);

}
}

// Sets the Tag property on a Node object to false
// when the MouseEnter event is called on the Node's SpotPanel.
// The binding on the Stroke will change to a Transparent brush.
private void Node_MouseLeave(object sender, MouseEventArgs e)
{
SetTagVisible(sender as UIElement, false);
}

// Used in the above two methods to change the node's Tag property to a specified value.
private void SetTagVisible(UIElement uielement, bool visible)
{
if (Northwoods.GoXam.Part.FindAncestor<Palette>(uielement) == null)
{
SpotPanel sp = uielement as SpotPanel;
if (sp == null) return;
Node n = Northwoods.GoXam.Part.FindAncestor<Node>(sp);
if (n == null) return;
n.Tag = visible;
if (visible)
{
Globals.Instance.ActiveNode = n;
Globals.Instance.ActiveSpotPanel = sp;
}
else
{
Globals.Instance.ActiveNode = null;
Globals.Instance.ActiveSpotPanel = null;
}
}
}

Enjoy

Thanks for the additional sample code.