Introduction

While investigating drag-and-drop using Windows Presentation Foundation, I found that most of the examples dealt with a single control and data type. In this article, I present an intra-application drag-and-drop framework in C# that supports multiple controls and data formats. Custom cursors and adorners are also supported.

The following functionality is covered in this article and supported by the framework.

Rearrange TabItems in a TabControl

Drag TabItems from one TabControl to another

Drag TreeViewItems to different locations within a TreeView

Drag TreeViewItem leaves to a ListBox

Drag ListBoxItems to new locations within a ListBox or to another ListBox

Drag ListBoxItems to a TreeView

Drag Buttons between ToolBars

Drag Buttons from a Canvas to a ToolBar or from a ToolBar to a Canvas

Move TextBlock, Rectangle and Button elements within a Canvas or to another Canvas

Create a TextBlock by dragging highlighted text in a RichTextBox to a Canvas

Insert text in a RichTextBox by dragging an element from a Canvas to a RichTextBox

Create TabItems, TreeViewItems and ListBoxItems by dragging a file or files from Windows Explorer

Drag any of the above items to a "trash" area to delete them (except Explorer files)

Custom cursors

Default adorner

Background

A drag-and-drop operation can be viewed as a data transfer from a data provider (the drag source) to a data consumer (the drop target). The data itself is passed between the data provider and data consumer using a mechanism similar to that of the Windows clipboard.

I'm not going to cover drag-and-drop fundamentals as I feel this topic has been sufficiently covered elsewhere. Here are a few links you may wish to investigate.

Framework Overview

The goal of the framework is to encapsulate the common intricacies of drag-and-drop so you just need to focus on the data you are dragging and dropping. The framework comprises base implementations of a data provider and data consumer, a drag manager, a drop manager, a default adorner and a few utility methods. You'll find the framework files in the DragDropFramework subdirectory of the project.

Data Providers and Consumers

Data providers are most often defined as a source container and a source object, and data consumers are most often defined as a destination container and a drop target. Consider the following list of data providers.

TabControl container/TabItem object

ListBox container/ListBoxItem object

TreeView container/TreeViewItem object

Canvas container/Button object

Canvas container/TextBlock object

Now consider the following list of data consumers.

TabControl container/TabItem object

ListBox container/ListBoxItem object

TreeView container/TreeViewItem object

Canvas container/Button object

Canvas container/TextBlock object

Most of the time, controls such as the TabControl, TreeView and ListBox will only provide and consume TabItems, TreeViewItems and ListBoxItems, respectively. However, in this article, the Canvas control provides and accepts several different object types.

The data provider and consumer files are kept in the DragDropFrameworkData subdirectory of the project.

Drag Manager

It is the framework user's responsibility to create an instance of the drag manager by specifying the container to be monitored (e.g. TabControl, Canvas, etc.) and the data to be dragged. The data to be dragged is defined by a class that extends the DataProviderBase class.

A drag operation begins when the user clicks on and drags an object defined by a data provider class. The drag manager automatically deals with housekeeping chores such as hooking events, displaying the correct cursor and showing the adorner.

Drop Manager

It is the framework user's responsibility to create an instance of the drop manager by specifying the container to be monitored (e.g. TabControl, Canvas, etc.) and the data to accept. The data to accept is defined by a class that extends the DataConsumerBase class.

When a user drags an object over a drop container, there are four events that can be triggered, as defined by the WPF drag-and-drop implementation. Here is a list of these events:

Drag Enter

Drag Over

Drag Leave

Drop

Each of these events provides the framework user with the opportunity to give feedback to the user by returning the appropriate DragDropEffects value. This value is used in the drag manager to display the appropriate cursor.

In the case of the Drop event, the returned DragDropEffects value indicates the operation that was finally performed (e.g. Move, Link or Copy).

We'll examine the data consumer in more depth later on in the article.

ListBox Data Provider

We'll start by looking at the ListBoxItem data provider. The following code shows the completed ListBoxDataProvider class.

A data provider in most cases extends the DataProviderBase class and must always implement the interface IDataProvider. A minimum data provider class must implement three methods. First, the constructor must pass the name given to the data to the base constructor. Second, AllowedEffects must return which of the four effects will be used. In most cases, WPF/Windows ANDs the effects returned by drop manager's events with the value of AllowedEffects. Third, DataProviderActions returns which methods to call in the data provider implementation.

AllowedEffects

The DragDropEffects Move, Copy and Link help determine which cursor should be displayed during a drag-and-drop operation. The standard cursors for these operations are displayed when dragging files within Windows Explorer, and the Shift, Ctrl and Alt keys are used to modify the drag operation.

DataProviderActions

The QueryContinueDrag event is specified by the WPF Drag-and-Drop implementation. However, it is encapsulated by the drag-and-drop framework implementation. By defining the QueryContinueDragDataProviderAction, DataProviderBase makes available the state of the Shift, Ctrl, Alt and Esc keys during a drag operation through the KeyStates and EscapedPressed properties, respectively.

The GiveFeedback event is also specified by the WPF Drag-and-Drop implementation. However, it too is encapsulated by the drag-and-drop framework implementation. By defining the GiveFeedbackDataProviderAction and writing the DragSource_GiveFeedback method, you can control the cursor that is displayed while a drag is in progress.

Unparent Method

In order for a ListBoxItem to be inserted into another Items collection, it must first be removed from its current Items collection. After the user does a drop, Unparent is called from a method in the data consumer to remove the dropped item from its old collection so it can be added to a new parent.

Creating the Drag Manager and ListBox Data Provider

The following code segment shows how the ListBoxDataProvider is created and passed to the drag manager along with the ListBox to monitor.

Note how ListBox and ListBoxItem are used as type parameters TContainer and TObject, respectively, when creating the data provider instance.

In order for a drag operation to begin, the user must click on an item of type ListBoxItem contained in a ListBox, as defined by the ListBoxDataProvider constructor. When the user drags such an object, its data is named "ListBoxItemObject," as passed to the constructor, and listBoxDataProvider is the data.

Note that the drag data is retrieved by the data consumer using its name, in this case "ListBoxItemObject." It's important to realize that the data object name used when creating the data provider class instance must match the data object name used when creating the data consumer.

Also note that the class instance can only be used by the program that created that data as the pointers would be invalid for any other program.

ListBox Data Consumer

We'll continue by looking at the ListBoxItem data consumer. The following code shows the completed ListBoxDataConsumer class.

A data consumer in most cases extends the DataConsumerBase class and must always implement the interface IDataConsumer. A minimum data consumer class must implement three methods. First, the constructor must pass the names given to the data to the base constructor. Second, DataConsumerActions returns which methods to call in the data consumer implementation. Third, in order to complete a drop, the method DropTarget_Drop must be implemented to perform the actions associated with the drop.

The work is done in the DragOverOrDrop method, which is called from DropTarget_DragEnter, DropTarget_DragOver and DropTarget_Drop. Normally all data consumer classes are written this way.

DragOverOrDrop

The first step is to retrieve the data being dragged. If dataProvider is not null, it is an instance of ListBoxDataProvider. The source container and source object are available using properties defined by the interface IDataProvider. Next get the drop container and drop object. If the source object is being dragged over an empty area of the list box, dropTarget will be null.

bDrop is true when the object is dropped; in other words the user has released the left mouse button. After a drop has happened, the source object is Unparented and either added to the drop container's collection (when dropTarget is null) or inserted before the drop target.

DropTarget_DragEnter and DropTarget_DragLeave

DropTarget_DragEnter is called when an object is dragged into a drop container and DropTarget_DragLeave is called when the object is dragged out of the drop container. You may wish to highlight the border of a ListBox when an object is dragged into the ListBox and return the border to a normal color when the object is dragged out of the ListBox. The DragEnter and DragLeave methods would be a good choice for implementing this kind of behavior.

In order for the correct cursor to be displayed by DragSource_GiveFeedback, e.Effects must be set to the proper value and e.Handled must be set to true. These are requirements of the WPF Drag-and-Drop implementation.

Note that the e.Effects value returned by DropTarget_dragEnter is masked by the value returned by the data provider's AllowedEffects. Furthermore, the e.Effects value returned by DropTarget_DragLeave is the value passed to DropTarget_DragEnter in both e.Effects and e.AllowedEffects and is not masked by the data provider's AllowedEffects. This behavior is defined by the WPF Drag-and-Drop implementation.

DropTarget_DragOver

DropTarget_DragOver is called many times as an object is dragged over a drop container. In order for the correct cursor to be displayed by DragSource_GiveFeedback, e.Effects must be set to the proper value and e.Handled must be set to true. These are requirements of the WPF Drag-and-Drop implementation.

DropTarget_Drop

When the user drops an object, DropTarget_Drop is called. Like the three DropTarget_* methods before, e.Effects must be set to the proper value and e.Handled must be set to true.

The e.Effects value returned by DropTarget_Drop is passed to the data provider's DoDragDrop_Done method, if it is provided. When moving a file, for example, the file would be copied to its destination by DropTarget_Drop and the original file would be deleted by DoDragDrop_Done after a successful copy.

Creating the Drop Manager

The following code segment shows how the ListBoxDataConsumer is created and passed to the drop manager along with the ListBox instance to monitor.

Remember how ListBox and ListBoxItem were used as type parameters when creating the data provider instance? The same two types must be used to create the data consumer instance. Note that the data format name passed to the ListBoxDataConsumer constructor is the same as the one passed to the ListBoxDataProvider. These are requirements for the ListBoxDataConsumer to consume data provided by the ListBoxDataProvider.

Quick Recap

To establish the overall flow, let's quickly recap what we've covered so far.

Data Provider

A data provider class is written which handles the source container type (ListBox) and the source object type (ListBoxItem). An instance of the data provider class is created which defines the data object's name ("ListBoxItemObject").

Drag Manager

A drag manager instance is created, passing the source container to monitor (ListBox instance) and an instance of the data provider class.

Data Consumer

A data consumer class is written which handles the drop container type (ListBox) and drop target type (ListBoxItem) to monitor. An instance of the data consumer class is created which defines the data object's name ("ListBoxItemObject").

Drop Manager

A drop manager instance is created, passing the drop container to monitor (ListBox instance) and an instance of the data consumer class.

The Flow

The drag manager detects when an object starts to be dragged and checks its list of data providers. If a match is found, it uses the class of the matching data provider as the drag data and initiates a drag operation by calling the WPF method DoDragDrop.

When an object is dragged into a container monitored by a drop manager, the appropriate method is called (DropTarget_Enter, then DropTarget_DragOver multiple times) which ends up calling DragOverOrDrop. DragOverOrDrop looks for a data provider it recognizes, then returns the appropriate value in e.Effects so the correct cursor is displayed by the data provider's DragSource_GiveFeedback method.

When the object is dropped, DragOverOrDrop is called a final time with bDrop set to true so the source object is Unparented and either inserted or added to the drop container's Items list.

Different Data Formats

Similar to working with clipboard data, the more data formats provided during a drag operation, the better. By default the drag manager sets one data format, which is the DataProvider class. The CanvasDataProvider overrides the default SetData method, shown below, so it can add a string data format.

By adding the string format, Rectangle, TextBlock and Button objects can be dragged from the canvas to the rich text box to insert text. Note how the first call to SetData, which adds the default data, is called with the SourceDataFormat string and a reference to the DataProvider class.

The second call to SetData is made to set the string data as long as textString isn't null.

Trash Data Consumer

The trash data consumer is the simplest data consumer implementation. To delete an object, as shown below, it simply Unparents all data that implements the IDataProvider interface.

Other Data Providers and Data Consumers

There is a total of twelve DataProvider/DataConsumer files in the project's DragDropFrameworkData directory. I'll take a little time and point out features that are unique to each implementation.

CanvasButtonConsumer.cs

This data consumer implementation is attached to tool bars and consumes buttons from the canvas. It's interesting that the button cannot simply be Unparented from the canvas and moved to the tool bar; a new copy of the button must be made. Try using the same button and you'll see that a selection box is drawn around the button once it's moved to the tool bar.

When a button is dragged on top of another button in the tool bar, a link cursor is displayed. The link cursor indicates that the button will be inserted before the target button. When a button is dragged over a tool bar's empty space, a regular non-link cursor is displayed; when dropped it will be the last button of the tool bar.

CanvasData.cs

We already looked at how the canvas data provider adds a string data format so that when an object is dragged from the canvas to the rich text box, text is inserted.

In the CanvasDataProvider implementation, the AddAdorner method is overridden and returns true so that objects dragged from the canvas have the default adorner.

Another unique feature of the canvas data provider and consumer is that dragged objects are placed at specific coordinates on the canvas when dropped. When an object is dragged, the point where the left mouse was clicked, relative to the object to be dragged, is saved in the StartPosition property. Later when the object is dropped, the StartPosition is subtracted from the point on the canvas where the left mouse button was released so the relationship of the mouse pointer to the object is maintained.

Note that data provider and data consumer instances are created for each object type (TextBlock, Rectangle and Button).

FileDropConsumer.cs

When a single file is dragged from Windows Explorer, its type is FileNameW and when multiple files are dragged, the type used is FileDrop. The actual data format in both cases is a string array. See in the following example how an instance of the FileDropConsumer is created:

// Used by TabControl, TreeView and ListBox.
// This data consumer allows items to be created
// from a file or files dragged from Windows Explorer.
FileDropConsumer fileDropDataConsumer =
new FileDropConsumer(newstring[] {
"FileDrop",
"FileNameW",
});

The above code shows a FileDropConsumer instance that consumes data formats FileDrop and FileNameW (as passed to the constructor). When an object is dragged over a target container, the search for supported formats is done in the order the formats are specified in the constructor. In this case FileDrop is searched for before FileNameW.

FileDropConsumer was written to be used with a TabControl, ListBox and TreeView. The following code shows the implementation of the DragOverOrDrop method.

Notice how the type of the sender is checked for one of the supported controls. When a drop is performed, an item corresponding to the sender's type is created and either inserted or added to the target control.

ListBoxData.cs

The ListBox data provider and consumer are generic in nature and were used as examples earlier in the article.

ListBoxToTreeView.cs

An object is dropped on a TreeView in one of three ways. The drop can occur over an empty area of the TreeView; in other words the object isn't dropped on a TreeView item. In this case, an item is added to the end of the TreeView. When an object is dropped on a TreeView item, the shift key can be used to modify the drop behavior. When the shift key is pressed, the object is added as a sibling of the drop target; otherwise it is added to the drop target as a child.

Note that a new TreeViewItem is created and the ListBoxItem's content is copied to it.

StringToCanvasTextBlock.cs

A text selection dragged from the rich text box has System.String as one of the available data formats. A StringToCanvasTextBlock instance is created that looks for that data type and adds a TextBlock to the canvas at the point where the left mouse button is released.

TabControlData.cs

The TabControl data provider and consumer allow tabs to be rearranged within the same control and tabs to be moved from one TabControl to another. When tabs within the same control are being rearranged and the widths of the source and target tabs are different, there is a tendency for the two tabs to oscillate back and forth during the move. The TabControlDataConsumer examines these widths and moves the tabs after there is no chance of oscillation.

When there is insufficient width to display tabs side-by-side, the TabControl will stack the tabs. This data consumer implementation does not address the oscillation that can occur when tabs are stacked.

The TabControl data provider demonstrates the use of custom cursors. When a tab is being moved from one control to another, a 'page' cursor is displayed. When the destination is forbidden, a 'page-not' cursor is displayed. The normal arrow cursor is used when tabs are being rearranged within the same control.

ToolbarButtonToCanvasButton.cs

Similar to the CanvasButtonConsumer discussed earlier, a copy of the tool bar button is made before being placed on the canvas. The reason a copy is made is because if the tool bar button were reused, there wouldn't be a border around the button once placed on the canvas. Plus a reused button would display a tool bar style selection box around the 'button' when the mouse hovers over it.

ToolBarData.cs

As described in the CanvasButtonConsumer, the cursor changes depending on whether a button object is over another button or the tool bar's empty space. When the drag cursor displays a link, the button will be inserted before the drop target, otherwise the button is added as the last button of the tool bar.

TrashConsumer.cs

The TrashConsumer was discussed earlier in the article.

TreeViewData.cs

As discussed above in ListBoxToTreeView, an object is added to a TreeView in one of three ways. An object dropped on empty space is added to the end of the TreeView. When dropped on a TreeViewItem, the object is added as a child of the TreeViewItem. However, when the shift key is being pressed, the object is added as a sibling of the drop target.

TreeViewToListBox.cs

Similar to the discussion above, when a TreeViewItem is dropped in a ListBox, the relevant information is copied from the TreeViewItem to a new ListBoxItem and then either inserted or added to the control.

Points of Interest

Boundary Conditions

As programmers, we know the importance of testing boundary conditions. As an example, let's look at both the ListBox and the TreeView.

Click on a ListBoxItem in the middle of a list a couple of pixels from either the top or bottom border. Now drag toward that border and into the neighboring ListBoxItem. Notice that the neighboring ListBoxItem becomes selected.

Repeat the same exercise, however this time selecting a TreeViewItem instead.

You should note that unlike the neighboring ListBoxItem that was selected, the neighboring TreeViewItem is not selected. There is a special case in the drag manager that saves the ListBox source object in the PreviewMouseMove event, as the source object can change between the PreviewMouseLeftButtonDown event and the PreviewMouseMove event when dealing with a ListBox.

Another boundary condition to test is clicking on a source object a couple of pixels from the container's edge and dragging outside of the container's boundary.

COMException crossed a native/managed boundary

Be prepared to experience an exception while running a debug version if you drag outside of the test application or Visual Studio. The exception can be silenced by unchecking 'Break when exceptions cross AppDomain or managed/native boundaries' found in Tools | Options... | Debugging | General.

Quick Adorners for All

You can quickly enable adorners for everything by changing AddAdorner to return true in DataProviderBase.cs.

PRINT2BUFFER and PRINT2OUTPUT Conditional Compile Debug Constants

There are two conditional compile constants used to provide debug information. When PRINT2BUFFER is defined, a character is appended to buf0 on entry to an event, and a character is appended to buf1 upon exit from the event. After the drag-and-drop operation is complete, the two buffers are compared and the results are written to Visual Studio's Output window. If the two buffers differ, that indicates a reentrancy issue. You must keep your code short and efficient to avoid such issues.

Conclusion

Upon completing this framework, I asked myself whether there was a net gain using the drag-and-drop framework interface as opposed to directly using Microsoft's drag-and-drop interface. After multiple projects I decided that there indeed was an advantage to using this framework over WPF's native drag-and-drop. There is ramp up involved no matter which choice you make. However, by encapsulating the WPF nuances once and for all within the drag-and-drop framework, I feel I'm able to more easily concentrate on what needs to be dragged and dropped.

When I short-circuit it to return 'true' no matter what, I end up receiving the error message, 'you can't do this while ItemsSource is in use' during the actual attempt to add the listBoxItem to the target listBox.

I'm not sure if the developer or anyone else still follows this, but help would of course be awesomely appreciated.

Hello!
I have a little issue with a little change I want to do. I want to be able to drag'N'drop from a treeview to another but instead of moving the treeviewItem, I want to copy it.

By the way and going a little bit deeper, is it possible to make the copy only at the same child level of the original treeview? Example: I have this structure on the original treeview:

--parent
-- child 1
-- child 2
-- child 3
-- child 1 of child 3

The idea is that if I copy the child 2 to the other treeview that it could only be copied nested to the parent. Another example with the same data could be when the child 1 of child 3 is copied that it could be only copied as child of the elements child 1, child 2, child 3

Thank you for this article!
I have one question. If I put into TabItem's Header not simply text, but anything else (TextBox or Path), Drag&Drop do not work in area of this TextBox or Path, i.e. I cannot Drag the TabItem. What can I do to make it work?

I haven't investigated it much yet, but this approach does not work while binding data through the ItemsSource collection with ListBoxes. This isn't much use if I can't incorporate it with data within the MVVM pattern. Is anyone else using this with bindings? Maybe I'm missing something but I don't think so. I can remove my binding from the ListBoxes and just hardcode ListBoxItems into the XAML as the source code project does and drag and drop works. However, if I try to bind to a List collection it does not. What I'm seeing is when the PreviewMouseLeftButtonDown event, the source and the sender are both coming across as ListBox types instead of one as ListBox and the other as a ListBoxItem. When the ListBoxItems are hardcoded in XAML, they come across as ListBoxItem as the sender and ListBox as the source. Below is my source. Any ideas?

The ListBox control, when having all the contents specified as ListBoxItems inside the XAML seems to emit MouseOver events with e.Source = ListBoxItem. This is why you need to instantiate ListBoxDataProvider like in the example in the article. The DragManager then checks the e.Source is of the ListBoxItem, prepares the 'overlay ghost'/adorner for it, and then begins to animate it accordingly.

However, when the ListBox control is bound and uses itemtemplate/datatemplate - the MouseOver events reported have e.Source = ... the ListBox. Let me say that again: in my application, I always get sender==e.Source==theListBox, pure referenceequals. This causes the typechecks against ListBoxItem in the DragManager to fail and the whole mechanism gets disabled. To force it running, your have to instantiate ie. ListBoxDataProvider or ListBoxDataProvider - and it would run! - but it will cause THE WHOLE ListBox to be dragged, not the item selected.

The only way to solve it, is to adjust the DragManager implementation to NOT use the e.Source directly, but, in case of working with controls like ListBox - to intelligently "translate" the e.OriginalSource into a proper-visual-parent-object-of-your-choice, and then use that in all places that the original DragManager used the e.Source..

The point is, that the ListBox *still* uses the ListBoxItem internally to host your databound datatemplate instances, but for reasons unknown, sets the e.Source improperly. Knowing the basic internal structure of the listbox - you can scan from OriginalSource upwards to the NOTfirst ancestor-ListBox and then immediatelly downwards to the first ancestor-ListBoxItem. You cannot scan to the first listbox and you must scan upto the 'e.Source' or the 'sender' because it would malfunction if your datatemplate contained another listbox on the path between the actual LB and the OrigSource but that are gory details. The workaround worked for me, have fun usingit.

In your opinion, how would you implement in WPF, in an app, if
I like to drag a item from a vertical list of items (on the left of the app)
and drop it into a slot of a stack and record its position (on the right of screen).
The stack is a picture of a stack for the user to visualize to insert an item.

I don't totally follow what you're trying to do on the right side. However, on the right side instead of using one list box with multiple items, how about using multiple list boxes with one or no items? The borders of the multiple list boxes would suggest to the user the multiple positions on the stack.

Thanks you so much for your topic. I have already researched the subject as well as WPF technology. I am studying How to "Drag and Drop" a object in a my project, now. I hope that you will send program structure /Class structure or general diagram about the program. It’s will help me having right way and easy to approach it.
Best Regard.

In the article, refer to the section titled "Different Data Formats." Also check the threads started by Lubomir Kovac and wcomeaux. There should be enough information to get you started on the right track.

First you need to figure out why the correct drag/drop icon is not being displayed. Make sure that whatever e.Effects is set to (in DragOverOrDrop) is handled in the data provider's DragSource_GiveFeedback method.

If your code works by only changing bDrop to true, that means that dropObject and sender are valid.

Thanks for the reply Ron, I guess the main problem for me is I wasn't able to figure out why drag/drop icon is not being displayed properly (accepting it).

I was playing around with your smorgasbord example, also unable to get the canvas to accept (show drag/drop icon properly) the listview item.

I defined the PRINT2OUTPUT, when I drag the listview item into the canvas, the "E" enter event was never fired, since the program didn't think it had a "drag enter", after all it was a can't drop icon shown.

Would you happen to have an idea to why it wouldn't accept it? I have been trying for days now, your help is very much appreciated.

A prerequisite to DropTarget_DragEnter (the E event) being called is DataConsumerActions must have the DataConsumerActions.DragEnter flag ORed in (see its get method). However, note that DropTarget_DragEnter does not have to be called. An example of when you'd want to call both DropTarget_DragEnter and DropTarget_DragLeave is when you'd want to highlight a border when an object is dragged over the canvas control, and return the border to normal when the object leaves the canvas.

You need to implement DropTarget_DragOver so the icon changes when dragging over the control, and DropTarget_Drop so a new object can be created for the canvas when the user drops an object there. So make sure that both DataConsumerActions.DragOver and DataConsumerActions.Drop flags are returned by DataConsumerActions.

If all you have to do is force bDrop to true (I assume when DragOverOrDrop is called from DropTarget_DragOver), I would think that the correct object is being dragged.

I finally figured out the problem, the main problems lies in the provider and consumer.
During the setup of drag and drop, I used the provider of the ListBoxData.cs, which is a generic of where TContainer : ItemsControl and where TObject : ListBoxItem. This allowed my ListBoxData to be type of ListBoxDataProvider.

That being side, the consumer of the canvas has the where TContainer : Canvas, where TObject : UIElement. and during drag over on the canvas, we use GetData(e) and try to cast it to a CanvasDataProvider<Canvas, UIElement>, which will always return null and not go any further in the drag/drop code.

The I fixed this by creating my own CanvasConsumer that has TContainer : Canvas and TObject : ListBoxItem.

Hi, I facing a very similar scenario, can you post that custom CanvasConsumer? In this case, I'm trying to drop some custom UserControls, but if they came from a list they will be treated as ListBoxItem, won't they?

This was pretty long ago, but after re-reading the problem, i finally remember what i did, I don't have that piece of code of the custom canvas consumer anymore, but I can tell you all I did was copy and paste like the CanvasButtonConsumer.cs and changed the TObject from button to listbox, and change the properties and methods accordingly to how I wanted.

I'm not aware of any data-based drag/drop modifications to this article other than what has been discussed here in the messages.

I'd approach it as described in the response to wcomeaux. You could call data.SetData one or more times and include a custom data type when an object is dragged. It's then up to the drop target to correctly interpret the data.

The Drag-and-Drop Smorgasbord actually does support different data types. Trace the code used when dragging from the canvas to the rich text box. Instead of calling data.SetData with a string, try defining your own data type and calling one of the following:

I made one change to your code which caused this. What I found was that if I used an ItemSource to populate my ListBox, the items would not drag, but if I just populated the listBox by adding to the items, it worked. I just found that my fix caused this problem. Can you try populating your ListBox via an itemSource and seeing if you can drag from it?

What I found was in the DataProviderBase IsSupportedContainerAndObject method. When I used an ItemSource, the gragSourceObject was a TSourceContainer, not a TSourceObject. I managed to identify that, but I found that the dragSourceObject is used to render in the adorner. The defult adorner it renders the items, not the selectedItems. Can I subclass your adorner just for this situation? How do I use a specific adorner for just this circumstance ? (assuming this is how I should fix it)

Thanks for the awesome code!. I'm wondering how to make this a bit more generic. It would make sense to drag and drop data around a UI as opposed to concrete elements. Is this something that has been considered before taking the approach defined here? I'm interested in a data-based solution and am trying to figure out just what needs to change to accomodate this.

I developed some drag-drop code for a project I am working on. In the project I am mostly dragging tabs among multiple tab controls, and tree view items to rearrange them within a tree view. I decided to see how generic I could make the drag-drop code, and the Smorgasbord was born.

As you can see, my application is more control-centric as opposed to data-centric as you discuss.

You may wish to look at the files in the DragDropFrameworkData directory that pertain to the canvas. Controls that are dragged off the canvas are represented by text strings in addition to the control itself. When a text string is dropped onto the canvas a text block is created. When a canvas control is dropped onto the rich text box text is inserted. You'll note in CanvasData.cs that data.SetData is called twice making two types of data available to a drop target.

The Smorgasbord could be modified to represent the dragged object (or objects in the case of a tree view item hierarchy) in XML (for example). When dropped it would be up to the drop target to interpret the XML.

Dragged data can be represented by multiple formats so the drop target can choose the most appropriate format to use. The concept is similar to copying a selection from a browser or copying a image (e.g. using Ctrl-C); there are multiple data formats available on the clipboard and when pasted it is up to the application where the paste is performed to choose the most appropriate format.