| Comments

One of the cool things I came across the in the October 2009 Silverlight Toolkit release was the addition of drag-n-drop targets for some of the core controls.  Now I know you are thinking great, another drag-n-drop useless control?!? and you’d be wrong.  I’m talking about things that make it easy to do do things like moving items from one list box to another, without writing code, but with it actually doing what you expect.

Let’s take the simplest example here: ListBox and moving items from one to another.  Using Expression Blend I’ve set up my XAML to be like this:

   1: <StackPanel Orientation="Horizontal" Margin="10">
   2:     <ListBox Width="200" Height="500" x:Name="FromBox" DisplayMemberPath="FullName"/>
   3:     <ListBox Width="200" Height="500" x:Name="ToBox" DisplayMemberPath="FullName"/>
   4: </StackPanel>

Behind the scenes I have a simple class which returns an ObservableCollection<Person> and binds the results to my FromBox.  Here’s the full simple class:

   1: using System.Collections.ObjectModel;
   3: namespace SilverlightApplication105
   4: {
   5:     public class People
   6:     {
   7:         public static ObservableCollection<Person> GetListOfPeople()
   8:         {
   9:             ObservableCollection<Person> ppl = new ObservableCollection<Person>();
  10:             for (int i = 0; i < 15; i++)
  11:             {
  12:                 Person p = new Person() { Firstname = "First " + i.ToString(), Lastname = "Last " + i.ToString() };
  13:                 ppl.Add(p);
  14:             }
  15:             return ppl;
  16:         }
  17:     }
  19:     public class Person
  20:     {
  21:         public string Firstname { get; set; }
  22:         public string Lastname { get; set; }
  23:         public string FullName
  24:         {
  25:             get
  26:             {
  27:                 return string.Concat(Firstname, " ", Lastname);
  28:             }
  29:         }
  30:     }
  31: }

And the code for my MainPage.xaml.cs:

   1: using System.Windows;
   2: using System.Windows.Controls;
   4: namespace SilverlightApplication105
   5: {
   6:     public partial class MainPage : UserControl
   7:     {
   8:         public MainPage()
   9:         {
  10:             InitializeComponent();
  11:             Loaded += new RoutedEventHandler(MainPage_Loaded);
  12:         }
  14:         void MainPage_Loaded(object sender, RoutedEventArgs e)
  15:         {
  16:             FromBox.ItemsSource = People.GetListOfPeople();
  17:         }
  18:     }
  19: }

Now I want to be able to drag an item from my FromBox to my ToBox.  I could do this in code, managing my index and moving things around, etc.  Or I can use something new from the toolkit!  Adding a reference in my Silverlight application to System.Windows.Controls.Toolkit, I then add two namespace declaration in my MainPage.xaml – here’s what the full XAML looks like now:

   1: <UserControl x:Class="SilverlightApplication105.MainPage"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   5:     mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
   6:     xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
   7:     xmlns:mswindows="clr-namespace:Microsoft.Windows;assembly=System.Windows.Controls.Toolkit">
   8:     <Grid x:Name="LayoutRoot">
   9:         <StackPanel Orientation="Horizontal" Margin="10">
  10:             <ListBox Width="200" Height="500" x:Name="FromBox" DisplayMemberPath="FullName"/>
  11:             <ListBox Width="200" Height="500" x:Name="ToBox" DisplayMemberPath="FullName"/>
  12:         </StackPanel>
  13:     </Grid>
  14: </UserControl>

Notice the xmlns:toolkit and xmlns:mswindows in the declarations.  Now I simply wrap the ListBox controls inside a ListBoxDropTarget control:

   1: <StackPanel Orientation="Horizontal" Margin="10">
   2:     <toolkit:ListBoxDragDropTarget mswindows:DragDrop.AllowDrop="True">
   3:         <ListBox Width="200" Height="500" x:Name="FromBox" DisplayMemberPath="FullName"/>
   4:     </toolkit:ListBoxDragDropTarget>
   5:     <toolkit:ListBoxDragDropTarget mswindows:DragDrop.AllowDrop="True">
   6:         <ListBox Width="200" Height="500" x:Name="ToBox" DisplayMemberPath="FullName"/>
   7:     </toolkit:ListBoxDragDropTarget>
   8: </StackPanel>

And when I run the application I get drag-n-drop item functionality from one list to the other, complete with a semi-opaque decorator as I drag the item:

ListBoxDragDropTarget image sample

Cool.  As I drag one item, it moves to the other.  But this can do more.  What if I just wanted to re-order items within a single ListBox?  This can do it as well…after all the ListBox can be both a drag *and* drop target.  However this ListBoxDragDropTarget doesn’t work with virtualized panels (which the ListBox uses by default.  So to do this you’d have to alter your ListBox ItemsPanelTemplate to include a regular StackPanel like so:

   1: <toolkit:ListBoxDragDropTarget mswindows:DragDrop.AllowDrop="True">
   2:     <ListBox Width="200" Height="500" x:Name="FromBox" DisplayMemberPath="FullName">
   3:         <ListBox.ItemsPanel>
   4:             <ItemsPanelTemplate>
   5:                 <StackPanel/>
   6:             </ItemsPanelTemplate>
   7:         </ListBox.ItemsPanel>
   8:     </ListBox>
   9: </toolkit:ListBoxDragDropTarget>

And then you’d be able to reorder using the drag/drop behavior of your mouse:

reorder sample image

Very cool.  What’s great about this is that while I’m using simple text, you can use whatever DataTemplate you may have in your ListBox and the same functionality works…even if I added an image to my Person class and added that to the template, the functionality still works and looks great for the user:

complex template sample image

As you can see the template follows the drop.  And the drop target location doesn’t have to match the same data template!  I can have my binding in the FromBox be a complex data template, but in the ToBox only choose to bind to a single property of the class.  Nice.  Here’s an animated view of this working or click here for a live sample:

This isn’t just for ListBox elements either.  Here are the other implementations:

  • ListBoxDragDropTarget
  • TreeViewDragDropTarget
  • DataGridDragDropTarget
  • DataPointSeriesDragDropTarget

Check out Jafar’s post for some samples on the other implementations to see how helpful they can be.

So what do you think?  Good?  Hope this helps some of your scenarios with ease.  Go Toolkit!

UPDATE: Download my project I used above here.

This work is licensed under a Creative Commons Attribution By license.

Please enjoy some of these other recent posts...