Commits

Anonymous committed 0b0bfbd

Replaced drag and drop library with actual code, because I need to add some features.

Comments (0)

Files changed (21)

UserProjects/sprucely/CSharp/Composer/Composer/Common/TypeUtilities.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections;
+using System.Reflection;
+
+namespace Composer.Common
+{
+	public class TypeUtilities
+	{
+		public static IEnumerable CreateDynamicallyTypedList(IEnumerable source)
+		{
+			Type type = GetCommonBaseClass(source);
+			Type listType = typeof(List<>).MakeGenericType(type);
+			MethodInfo addMethod = listType.GetMethod("Add");
+			object list = listType.GetConstructor(Type.EmptyTypes).Invoke(null);
+
+			foreach (object o in source)
+			{
+				addMethod.Invoke(list, new[] { o });
+			}
+
+			return (IEnumerable)list;
+		}
+
+		public static Type GetCommonBaseClass(IEnumerable e)
+		{
+			Type[] types = e.Cast<object>().Select(o => o.GetType()).ToArray<Type>();
+			return GetCommonBaseClass(types);
+		}
+
+		public static Type GetCommonBaseClass(Type[] types)
+		{
+			if (types.Length == 0)
+				return typeof(object);
+
+			Type ret = types[0];
+
+			for (int i = 1; i < types.Length; ++i)
+			{
+				if (types[i].IsAssignableFrom(ret))
+					ret = types[i];
+				else
+				{
+					// This will always terminate when ret == typeof(object)
+					while (!ret.IsAssignableFrom(types[i]))
+						ret = ret.BaseType;
+				}
+			}
+
+			return ret;
+		}
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/Composer.csproj

     <Reference Include="FluentNHibernate">
       <HintPath>..\lib\FluentNHibernate.dll</HintPath>
     </Reference>
-    <Reference Include="GongSolutions.Wpf.DragDrop">
-      <HintPath>..\lib\GongSolutions.Wpf.DragDrop.dll</HintPath>
-    </Reference>
     <Reference Include="GraphSharp">
       <HintPath>..\lib\GraphSharp.dll</HintPath>
     </Reference>
       <SubType>Code</SubType>
     </Compile>
     <Compile Include="Common\Collections\ReadOnlyDictionary.cs" />
+    <Compile Include="Common\TypeUtilities.cs" />
     <Compile Include="Graph\Initialization\CellTypes.cs" />
     <Compile Include="UI\Behaviors\CommandRelay.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DefaultDragHandler.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DefaultDropHandler.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DragAdorner.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DragDrop.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DragInfo.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DropInfo.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DropTargetAdorner.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DropTargetAdorners.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DropTargetHighlightAdorner.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\DropTargetInsertionAdorner.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\IDragSource.cs" />
+    <Compile Include="UI\Behaviors\DragAndDrop\IDropTarget.cs" />
+    <Compile Include="UI\ItemsControlExtensions.cs" />
     <Compile Include="UI\Screens\GraphEditor\LayoutAlgorithmFactory.cs" />
     <Compile Include="UI\Screens\GraphEditor\LayoutAlgorithm_Manual.cs" />
     <Compile Include="UI\Screens\ObjectExplorer\ObjectInfo.cs" />
     <Compile Include="ORM\Mappings.cs" />
     <Compile Include="ORM\SessionHelper.cs" />
     <Compile Include="ShellViewModel.cs" />
+    <Compile Include="UI\VisualTreeExtensions.cs" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AppBootstrapper.cs" />

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DefaultDragHandler.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Composer.Common;
+using System.Windows;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	public class DefaultDragHandler : IDragSource
+	{
+		public virtual void StartDrag(IDragInfo dragInfo)
+		{
+			int itemCount = dragInfo.SourceItems.Cast<object>().Count();
+
+			if (itemCount == 1)
+			{
+				dragInfo.Data = dragInfo.SourceItems.Cast<object>().First();
+			}
+			else if (itemCount > 1)
+			{
+				dragInfo.Data = TypeUtilities.CreateDynamicallyTypedList(dragInfo.SourceItems);
+			}
+
+			dragInfo.Effects = (dragInfo.Data != null) ?
+				DragDropEffects.Copy | DragDropEffects.Move :
+				DragDropEffects.None;
+		}
+
+		public virtual void Dropped(IDropInfo dropInfo)
+		{
+		}
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DefaultDropHandler.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Composer.Common;
+using System.Reflection;
+using System.Collections;
+using System.Windows.Controls;
+using System.Windows;
+using System.ComponentModel;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	class DefaultDropHandler : IDropTarget
+	{
+		public virtual void DragOver(IDropInfo dropInfo)
+		{
+			if (CanAcceptData(dropInfo))
+			{
+				dropInfo.Effects = DragDropEffects.Copy;
+				dropInfo.DropTargetAdorner = DropTargetAdorners.Insert;
+			}
+		}
+
+		public virtual void Drop(IDropInfo dropInfo)
+		{
+			int insertIndex = dropInfo.InsertIndex;
+			IList destinationList = GetList(dropInfo.TargetCollection);
+			IEnumerable data = ExtractData(dropInfo.Data);
+
+			if (dropInfo.DragInfo.VisualSource == dropInfo.VisualTarget)
+			{
+				IList sourceList = GetList(dropInfo.DragInfo.SourceCollection);
+
+				foreach (object o in data)
+				{
+					int index = sourceList.IndexOf(o);
+
+					if (index != -1)
+					{
+						sourceList.RemoveAt(index);
+
+						if (sourceList == destinationList && index < insertIndex)
+						{
+							--insertIndex;
+						}
+					}
+				}
+			}
+
+			foreach (object o in data)
+			{
+				destinationList.Insert(insertIndex++, o);
+			}
+		}
+
+		protected static bool CanAcceptData(IDropInfo dropInfo)
+		{
+			if (dropInfo.DragInfo.SourceCollection == dropInfo.TargetCollection)
+			{
+				return GetList(dropInfo.TargetCollection) != null;
+			}
+			else if (dropInfo.DragInfo.SourceCollection is ItemCollection)
+			{
+				return false;
+			}
+			else
+			{
+				if (TestCompatibleTypes(dropInfo.TargetCollection, dropInfo.Data))
+				{
+					return !IsChildOf(dropInfo.VisualTargetItem, dropInfo.DragInfo.VisualSourceItem);
+				}
+				else
+				{
+					return false;
+				}
+			}
+		}
+
+		protected static IEnumerable ExtractData(object data)
+		{
+			if (data is IEnumerable && !(data is string))
+			{
+				return (IEnumerable)data;
+			}
+			else
+			{
+				return Enumerable.Repeat(data, 1);
+			}
+		}
+
+		protected static IList GetList(IEnumerable enumerable)
+		{
+			if (enumerable is ICollectionView)
+			{
+				return ((ICollectionView)enumerable).SourceCollection as IList;
+			}
+			else
+			{
+				return enumerable as IList;
+			}
+		}
+
+		protected static bool IsChildOf(UIElement targetItem, UIElement sourceItem)
+		{
+			ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(targetItem);
+
+			while (parent != null)
+			{
+				if (parent == sourceItem)
+				{
+					return true;
+				}
+
+				parent = ItemsControl.ItemsControlFromItemContainer(parent);
+			}
+
+			return false;
+		}
+
+		protected static bool TestCompatibleTypes(IEnumerable target, object data)
+		{
+			TypeFilter filter = (t, o) =>
+			{
+				return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
+			};
+
+			var enumerableInterfaces = target.GetType().FindInterfaces(filter, null);
+			var enumerableTypes = from i in enumerableInterfaces select i.GetGenericArguments().Single();
+
+			if (enumerableTypes.Count() > 0)
+			{
+				Type dataType = TypeUtilities.GetCommonBaseClass(ExtractData(data));
+				return enumerableTypes.Any(t => t.IsAssignableFrom(dataType));
+			}
+			else
+			{
+				return target is IList;
+			}
+		}
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DragAdorner.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Media;
+using System.Windows.Documents;
+using System.Windows;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	class DragAdorner : Adorner
+	{
+		public DragAdorner(UIElement adornedElement, UIElement adornment)
+			: base(adornedElement)
+		{
+			m_AdornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
+			m_AdornerLayer.Add(this);
+			m_Adornment = adornment;
+			IsHitTestVisible = false;
+		}
+
+		public Point MousePosition
+		{
+			get { return m_MousePosition; }
+			set
+			{
+				if (m_MousePosition != value)
+				{
+					m_MousePosition = value;
+					m_AdornerLayer.Update(AdornedElement);
+				}
+			}
+		}
+
+		public void Detatch()
+		{
+			m_AdornerLayer.Remove(this);
+		}
+
+		protected override Size ArrangeOverride(Size finalSize)
+		{
+			m_Adornment.Arrange(new Rect(finalSize));
+			return finalSize;
+		}
+
+		public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
+		{
+			GeneralTransformGroup result = new GeneralTransformGroup();
+			result.Children.Add(base.GetDesiredTransform(transform));
+			result.Children.Add(new TranslateTransform(MousePosition.X - 4, MousePosition.Y - 4));
+
+			return result;
+		}
+
+		protected override Visual GetVisualChild(int index)
+		{
+			return m_Adornment;
+		}
+
+		protected override Size MeasureOverride(Size constraint)
+		{
+			m_Adornment.Measure(constraint);
+			return m_Adornment.DesiredSize;
+		}
+
+		protected override int VisualChildrenCount
+		{
+			get { return 1; }
+		}
+
+		AdornerLayer m_AdornerLayer;
+		UIElement m_Adornment;
+		Point m_MousePosition;
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DragDrop.cs

+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	public static class DragDrop
+	{
+		public static DataTemplate GetDragAdornerTemplate(UIElement target)
+		{
+			return (DataTemplate)target.GetValue(DragAdornerTemplateProperty);
+		}
+
+		public static void SetDragAdornerTemplate(UIElement target, DataTemplate value)
+		{
+			target.SetValue(DragAdornerTemplateProperty, value);
+		}
+
+		public static bool GetIsDragSource(UIElement target)
+		{
+			return (bool)target.GetValue(IsDragSourceProperty);
+		}
+
+		public static void SetIsDragSource(UIElement target, bool value)
+		{
+			target.SetValue(IsDragSourceProperty, value);
+		}
+
+		public static bool GetIsDropTarget(UIElement target)
+		{
+			return (bool)target.GetValue(IsDropTargetProperty);
+		}
+
+		public static void SetIsDropTarget(UIElement target, bool value)
+		{
+			target.SetValue(IsDropTargetProperty, value);
+		}
+
+		public static IDragSource GetDragHandler(UIElement target)
+		{
+			return (IDragSource)target.GetValue(DragHandlerProperty);
+		}
+
+		public static void SetDragHandler(UIElement target, IDragSource value)
+		{
+			target.SetValue(DragHandlerProperty, value);
+		}
+
+		public static IDropTarget GetDropHandler(UIElement target)
+		{
+			return (IDropTarget)target.GetValue(DropHandlerProperty);
+		}
+
+		public static void SetDropHandler(UIElement target, IDropTarget value)
+		{
+			target.SetValue(DropHandlerProperty, value);
+		}
+
+		public static IDragSource DefaultDragHandler
+		{
+			get
+			{
+				if (m_DefaultDragHandler == null)
+				{
+					m_DefaultDragHandler = new DefaultDragHandler();
+				}
+
+				return m_DefaultDragHandler;
+			}
+			set
+			{
+				m_DefaultDragHandler = value;
+			}
+		}
+
+		public static IDropTarget DefaultDropHandler
+		{
+			get
+			{
+				if (m_DefaultDropHandler == null)
+				{
+					m_DefaultDropHandler = new DefaultDropHandler();
+				}
+
+				return m_DefaultDropHandler;
+			}
+			set
+			{
+				m_DefaultDropHandler = value;
+			}
+		}
+
+		public static readonly DependencyProperty DragAdornerTemplateProperty =
+			DependencyProperty.RegisterAttached("DragAdornerTemplate", typeof(DataTemplate), typeof(DragDrop));
+
+		public static readonly DependencyProperty DragHandlerProperty =
+			DependencyProperty.RegisterAttached("DragHandler", typeof(IDragSource), typeof(DragDrop));
+
+		public static readonly DependencyProperty DropHandlerProperty =
+			DependencyProperty.RegisterAttached("DropHandler", typeof(IDropTarget), typeof(DragDrop));
+
+		public static readonly DependencyProperty IsDragSourceProperty =
+			DependencyProperty.RegisterAttached("IsDragSource", typeof(bool), typeof(DragDrop),
+				new UIPropertyMetadata(false, IsDragSourceChanged));
+
+		public static readonly DependencyProperty IsDropTargetProperty =
+			DependencyProperty.RegisterAttached("IsDropTarget", typeof(bool), typeof(DragDrop),
+				new UIPropertyMetadata(false, IsDropTargetChanged));
+
+		public static readonly DataFormat DataFormat = DataFormats.GetDataFormat("GongSolutions.Wpf.DragDrop");
+
+		static void IsDragSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+		{
+			UIElement uiElement = (UIElement)d;
+
+			if ((bool)e.NewValue == true)
+			{
+				uiElement.PreviewMouseLeftButtonDown += DragSource_PreviewMouseLeftButtonDown;
+				uiElement.PreviewMouseLeftButtonUp += DragSource_PreviewMouseLeftButtonUp;
+				uiElement.PreviewMouseMove += DragSource_PreviewMouseMove;
+			}
+			else
+			{
+				uiElement.PreviewMouseLeftButtonDown -= DragSource_PreviewMouseLeftButtonDown;
+				uiElement.PreviewMouseLeftButtonUp -= DragSource_PreviewMouseLeftButtonUp;
+				uiElement.PreviewMouseMove -= DragSource_PreviewMouseMove;
+			}
+		}
+
+		static void IsDropTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+		{
+			UIElement uiElement = (UIElement)d;
+
+			if ((bool)e.NewValue == true)
+			{
+				uiElement.AllowDrop = true;
+				uiElement.PreviewDragEnter += DropTarget_PreviewDragEnter;
+				uiElement.PreviewDragLeave += DropTarget_PreviewDragLeave;
+				uiElement.PreviewDragOver += DropTarget_PreviewDragOver;
+				uiElement.PreviewDrop += DropTarget_PreviewDrop;
+			}
+			else
+			{
+				uiElement.AllowDrop = false;
+				uiElement.PreviewDragEnter -= DropTarget_PreviewDragEnter;
+				uiElement.PreviewDragLeave -= DropTarget_PreviewDragLeave;
+				uiElement.PreviewDragOver -= DropTarget_PreviewDragOver;
+				uiElement.PreviewDrop -= DropTarget_PreviewDrop;
+			}
+		}
+
+		static void CreateDragAdorner()
+		{
+			DataTemplate template = GetDragAdornerTemplate(m_DragInfo.VisualSource);
+
+			if (template != null)
+			{
+				UIElement rootElement = null;
+				Window parentWindow = m_DragInfo.VisualSource.GetVisualAncestor<Window>();
+				UIElement adornment = null;
+
+				if (parentWindow != null)
+				{
+					rootElement = parentWindow.Content as UIElement;
+				}
+				if (rootElement == null)
+				{
+					rootElement = (UIElement)Application.Current.MainWindow.Content;
+				}
+
+				if (m_DragInfo.Data is IEnumerable && !(m_DragInfo.Data is string))
+				{
+					if (((IEnumerable)m_DragInfo.Data).Cast<object>().Count() <= 10)
+					{
+						ItemsControl itemsControl = new ItemsControl();
+						itemsControl.ItemsSource = (IEnumerable)m_DragInfo.Data;
+						itemsControl.ItemTemplate = template;
+
+						// The ItemsControl doesn't display unless we create a border to contain it.
+						// Not quite sure why this is...
+						Border border = new Border();
+						border.Child = itemsControl;
+						adornment = border;
+					}
+				}
+				else
+				{
+					ContentPresenter contentPresenter = new ContentPresenter();
+					contentPresenter.Content = m_DragInfo.Data;
+					contentPresenter.ContentTemplate = template;
+					adornment = contentPresenter;
+				}
+
+				if (adornment != null)
+				{
+					adornment.Opacity = 0.5;
+					DragAdorner = new DragAdorner(rootElement, adornment);
+				}
+			}
+		}
+
+		static bool HitTestScrollBar(object sender, MouseButtonEventArgs e)
+		{
+			HitTestResult hit = VisualTreeHelper.HitTest((Visual)sender, e.GetPosition((IInputElement)sender));
+			return hit.VisualHit.GetVisualAncestor<System.Windows.Controls.Primitives.ScrollBar>() != null;
+		}
+
+		static void Scroll(DependencyObject o, DragEventArgs e)
+		{
+			ScrollViewer scrollViewer = o.GetVisualDescendent<ScrollViewer>();
+
+			if (scrollViewer != null)
+			{
+				Point position = e.GetPosition(scrollViewer);
+				double scrollMargin = Math.Min(scrollViewer.FontSize * 2, scrollViewer.ActualHeight / 2);
+
+				if (position.X >= scrollViewer.ActualWidth - scrollMargin &&
+					scrollViewer.HorizontalOffset < scrollViewer.ExtentWidth - scrollViewer.ViewportWidth)
+				{
+					scrollViewer.LineRight();
+				}
+				else if (position.X < scrollMargin && scrollViewer.HorizontalOffset > 0)
+				{
+					scrollViewer.LineLeft();
+				}
+				else if (position.Y >= scrollViewer.ActualHeight - scrollMargin &&
+					scrollViewer.VerticalOffset < scrollViewer.ExtentHeight - scrollViewer.ViewportHeight)
+				{
+					scrollViewer.LineDown();
+				}
+				else if (position.Y < scrollMargin && scrollViewer.VerticalOffset > 0)
+				{
+					scrollViewer.LineUp();
+				}
+			}
+		}
+
+		static void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+		{
+			// Ignore the click if the user has clicked on a scrollbar.
+			if (HitTestScrollBar(sender, e))
+			{
+				m_DragInfo = null;
+				return;
+			}
+
+			m_DragInfo = new DragInfo(sender, e);
+
+			// If the sender is a list box that allows multiple selections, ensure that clicking on an 
+			// already selected item does not change the selection, otherwise dragging multiple items 
+			// is made impossible.
+			ItemsControl itemsControl = sender as ItemsControl;
+
+			if (m_DragInfo.VisualSourceItem != null && itemsControl != null && itemsControl.CanSelectMultipleItems())
+			{
+				IEnumerable<object> selectedItems = itemsControl.GetSelectedItems().Cast<object>();
+
+				if (selectedItems.Count() > 1 && selectedItems.Contains(m_DragInfo.SourceItem))
+				{
+					m_ClickSupressItem = m_DragInfo.SourceItem;
+					e.Handled = true;
+				}
+			}
+		}
+
+		static void DragSource_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+		{
+			// If we prevented the control's default selection handling in DragSource_PreviewMouseLeftButtonDown
+			// by setting 'e.Handled = true' and a drag was not initiated, manually set the selection here.
+			ItemsControl itemsControl = sender as ItemsControl;
+
+			if (itemsControl != null && m_DragInfo != null && m_ClickSupressItem == m_DragInfo.SourceItem)
+			{
+				if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
+				{
+					itemsControl.SetItemSelected(m_DragInfo.SourceItem, false);
+				}
+				else
+				{
+					itemsControl.SetSelectedItem(m_DragInfo.SourceItem);
+				}
+			}
+
+			if (m_DragInfo != null)
+			{
+				m_DragInfo = null;
+			}
+
+			m_ClickSupressItem = null;
+		}
+
+		static void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
+		{
+			if (m_DragInfo != null && !m_DragInProgress)
+			{
+				Point dragStart = m_DragInfo.DragStartPosition;
+				Point position = e.GetPosition(null);
+
+				if (Math.Abs(position.X - dragStart.X) > SystemParameters.MinimumHorizontalDragDistance ||
+					Math.Abs(position.Y - dragStart.Y) > SystemParameters.MinimumVerticalDragDistance)
+				{
+					IDragSource dragHandler = GetDragHandler(m_DragInfo.VisualSource);
+
+					if (dragHandler != null)
+					{
+						dragHandler.StartDrag(m_DragInfo);
+					}
+					else
+					{
+						DefaultDragHandler.StartDrag(m_DragInfo);
+					}
+
+					if (m_DragInfo.Effects != DragDropEffects.None && m_DragInfo.Data != null)
+					{
+						DataObject data = new DataObject(DataFormat.Name, m_DragInfo.Data);
+
+						try
+						{
+							m_DragInProgress = true;
+							System.Windows.DragDrop.DoDragDrop(m_DragInfo.VisualSource, data, m_DragInfo.Effects);
+						}
+						finally
+						{
+							m_DragInProgress = false;
+						}
+
+						m_DragInfo = null;
+					}
+				}
+			}
+		}
+
+		static void DropTarget_PreviewDragEnter(object sender, DragEventArgs e)
+		{
+			DropTarget_PreviewDragOver(sender, e);
+		}
+
+		static void DropTarget_PreviewDragLeave(object sender, DragEventArgs e)
+		{
+			DragAdorner = null;
+			DropTargetAdorner = null;
+		}
+
+		static void DropTarget_PreviewDragOver(object sender, DragEventArgs e)
+		{
+			DropInfo dropInfo = new DropInfo(sender, e, m_DragInfo);
+			IDropTarget dropHandler = GetDropHandler((UIElement)sender);
+			ItemsControl itemsControl = sender as ItemsControl;
+
+			if (dropHandler != null)
+			{
+				dropHandler.DragOver(dropInfo);
+			}
+			else
+			{
+				DefaultDropHandler.DragOver(dropInfo);
+			}
+
+			// Update the drag adorner.
+			if (dropInfo.Effects != DragDropEffects.None)
+			{
+				if (DragAdorner == null && m_DragInfo != null)
+				{
+					CreateDragAdorner();
+				}
+
+				if (DragAdorner != null)
+				{
+					DragAdorner.MousePosition = e.GetPosition(DragAdorner.AdornedElement);
+					DragAdorner.InvalidateVisual();
+				}
+			}
+			else
+			{
+				DragAdorner = null;
+			}
+
+			// If the target is an ItemsControl then update the drop target adorner.
+			if (itemsControl != null)
+			{
+				// Display the adorner in the control's ItemsPresenter. If there is no 
+				// ItemsPresenter provided by the style, try getting hold of a
+				// ScrollContentPresenter and using that.
+				UIElement adornedElement =
+					(UIElement)itemsControl.GetVisualDescendent<ItemsPresenter>() ??
+					(UIElement)itemsControl.GetVisualDescendent<ScrollContentPresenter>();
+
+				if (adornedElement != null)
+				{
+					if (dropInfo.DropTargetAdorner == null)
+					{
+						DropTargetAdorner = null;
+					}
+					else if (!dropInfo.DropTargetAdorner.IsInstanceOfType(DropTargetAdorner))
+					{
+						DropTargetAdorner = DropTargetAdorner.Create(dropInfo.DropTargetAdorner, adornedElement);
+					}
+
+					if (DropTargetAdorner != null)
+					{
+						DropTargetAdorner.DropInfo = dropInfo;
+						DropTargetAdorner.InvalidateVisual();
+					}
+				}
+			}
+
+			e.Effects = dropInfo.Effects;
+			e.Handled = true;
+
+			Scroll((DependencyObject)sender, e);
+		}
+
+		static void DropTarget_PreviewDrop(object sender, DragEventArgs e)
+		{
+			DropInfo dropInfo = new DropInfo(sender, e, m_DragInfo);
+			IDropTarget dropHandler = GetDropHandler((UIElement)sender) ?? DefaultDropHandler;
+			IDragSource dragHandler = GetDragHandler((UIElement)sender) ?? DefaultDragHandler;
+
+			DragAdorner = null;
+			DropTargetAdorner = null;
+			dropHandler.Drop(dropInfo);
+			dragHandler.Dropped(dropInfo);
+			e.Handled = true;
+		}
+
+		static DragAdorner DragAdorner
+		{
+			get { return m_DragAdorner; }
+			set
+			{
+				if (m_DragAdorner != null)
+				{
+					m_DragAdorner.Detatch();
+				}
+
+				m_DragAdorner = value;
+			}
+		}
+
+		static DropTargetAdorner DropTargetAdorner
+		{
+			get { return m_DropTargetAdorner; }
+			set
+			{
+				if (m_DropTargetAdorner != null)
+				{
+					m_DropTargetAdorner.Detatch();
+				}
+
+				m_DropTargetAdorner = value;
+			}
+		}
+
+		static IDragSource m_DefaultDragHandler;
+		static IDropTarget m_DefaultDropHandler;
+		static DragAdorner m_DragAdorner;
+		static DragInfo m_DragInfo;
+		static bool m_DragInProgress;
+		static DropTargetAdorner m_DropTargetAdorner;
+		static object m_ClickSupressItem;
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DragInfo.cs

+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	public interface IDragInfo
+	{
+		/// <summary>
+		/// Gets or sets the drag data.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// This must be set by a drag handler in order for a drag to start.
+		/// </remarks>
+		object Data { get; set; }
+
+		/// <summary>
+		/// Gets the position of the click that initiated the drag, relative to <see cref="VisualSource"/>.
+		/// </summary>
+		Point DragStartPosition { get; }
+
+		/// <summary>
+		/// Gets or sets the allowed effects for the drag.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drag handler in order 
+		/// for a drag to start.
+		/// </remarks>
+		DragDropEffects Effects { get; set; }
+
+		/// <summary>
+		/// Gets the mouse button that initiated the drag.
+		/// </summary>
+		MouseButton MouseButton { get; }
+
+		/// <summary>
+		/// Gets the collection that the source ItemsControl is bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		IEnumerable SourceCollection { get; }
+
+		/// <summary>
+		/// Gets the object that a dragged item is bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		object SourceItem { get; }
+
+		/// <summary>
+		/// Gets a collection of objects that the selected items in an ItemsControl are bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the control that initated the drag is unbound or not an ItemsControl, this will be empty.
+		/// </remarks>
+		IEnumerable SourceItems { get; }
+
+		/// <summary>
+		/// Gets the control that initiated the drag.
+		/// </summary>
+		UIElement VisualSource { get; }
+
+		/// <summary>
+		/// Gets the item in an ItemsControl that started the drag.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the control that initiated the drag is an ItemsControl, this property will hold the item
+		/// container of the clicked item. For example, if <see cref="VisualSource"/> is a ListBox this
+		/// will hold a ListBoxItem.
+		/// </remarks>
+		UIElement VisualSourceItem { get; }
+	}
+
+	/// <summary>
+	/// Holds information about a the source of a drag drop operation.
+	/// </summary>
+	/// 
+	/// <remarks>
+	/// The <see cref="DragInfo"/> class holds all of the framework's information about the source
+	/// of a drag. It is used by <see cref="IDragSource.StartDrag"/> to determine whether a drag 
+	/// can start, and what the dragged data should be.
+	/// </remarks>
+	public class DragInfo : IDragInfo
+	{
+		/// <summary>
+		/// Initializes a new instance of the DragInfo class.
+		/// </summary>
+		/// 
+		/// <param name="sender">
+		/// The sender of the mouse event that initiated the drag.
+		/// </param>
+		/// 
+		/// <param name="e">
+		/// The mouse event that initiated the drag.
+		/// </param>
+		public DragInfo(object sender, MouseButtonEventArgs e)
+		{
+			DragStartPosition = e.GetPosition(null);
+			Effects = DragDropEffects.None;
+			MouseButton = e.ChangedButton;
+			VisualSource = sender as UIElement;
+
+			if (sender is ItemsControl)
+			{
+				ItemsControl itemsControl = (ItemsControl)sender;
+				UIElement item = itemsControl.GetItemContainer((UIElement)e.OriginalSource);
+
+				if (item != null)
+				{
+					ItemsControl itemParent = ItemsControl.ItemsControlFromItemContainer(item);
+
+					SourceCollection = itemParent.ItemsSource ?? itemParent.Items;
+					SourceItem = itemParent.ItemContainerGenerator.ItemFromContainer(item);
+					SourceItems = itemsControl.GetSelectedItems();
+
+					// Some controls (I'm looking at you TreeView!) haven't updated their
+					// SelectedItem by this point. Check to see if there 1 or less item in 
+					// the SourceItems collection, and if so, override the control's 
+					// SelectedItems with the clicked item.
+					if (SourceItems.Cast<object>().Count() <= 1)
+					{
+						SourceItems = Enumerable.Repeat(SourceItem, 1);
+					}
+
+					VisualSourceItem = item;
+				}
+				else
+				{
+					SourceCollection = itemsControl.ItemsSource ?? itemsControl.Items;
+				}
+			}
+
+			if (SourceItems == null)
+			{
+				SourceItems = Enumerable.Empty<object>();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the drag data.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// This must be set by a drag handler in order for a drag to start.
+		/// </remarks>
+		public object Data { get; set; }
+
+		/// <summary>
+		/// Gets the position of the click that initiated the drag, relative to <see cref="VisualSource"/>.
+		/// </summary>
+		public Point DragStartPosition { get; private set; }
+
+		/// <summary>
+		/// Gets or sets the allowed effects for the drag.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drag handler in order 
+		/// for a drag to start.
+		/// </remarks>
+		public DragDropEffects Effects { get; set; }
+
+		/// <summary>
+		/// Gets the mouse button that initiated the drag.
+		/// </summary>
+		public MouseButton MouseButton { get; private set; }
+
+		/// <summary>
+		/// Gets the collection that the source ItemsControl is bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		public IEnumerable SourceCollection { get; private set; }
+
+		/// <summary>
+		/// Gets the object that a dragged item is bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		public object SourceItem { get; private set; }
+
+		/// <summary>
+		/// Gets a collection of objects that the selected items in an ItemsControl are bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the control that initated the drag is unbound or not an ItemsControl, this will be empty.
+		/// </remarks>
+		public IEnumerable SourceItems { get; private set; }
+
+		/// <summary>
+		/// Gets the control that initiated the drag.
+		/// </summary>
+		public UIElement VisualSource { get; private set; }
+
+		/// <summary>
+		/// Gets the item in an ItemsControl that started the drag.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the control that initiated the drag is an ItemsControl, this property will hold the item
+		/// container of the clicked item. For example, if <see cref="VisualSource"/> is a ListBox this
+		/// will hold a ListBoxItem.
+		/// </remarks>
+		public UIElement VisualSourceItem { get; private set; }
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DropInfo.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Collections;
+using System.Windows.Data;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	public interface IDropInfo
+	{
+		/// <summary>
+		/// Gets the drag data.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the drag came from within the framework, this will hold:
+		/// 
+		/// - The dragged data if a single item was dragged.
+		/// - A typed IEnumerable if multiple items were dragged.
+		/// </remarks>
+		object Data { get; }
+
+		/// <summary>
+		/// Gets a <see cref="DragInfo"/> object holding information about the source of the drag, 
+		/// if the drag came from within the framework.
+		/// </summary>
+		IDragInfo DragInfo { get; }
+
+		/// <summary>
+		/// Gets the mouse position relative to the VisualTarget
+		/// </summary>
+		Point DropPosition { get; }
+
+		/// <summary>
+		/// Gets or sets the class of drop target to display.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// The standard drop target adorner classes are held in the <see cref="DropTargetAdorners"/>
+		/// class.
+		/// </remarks>
+		Type DropTargetAdorner { get; set; }
+
+		/// <summary>
+		/// Gets or sets the allowed effects for the drop.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drop handler in order 
+		/// for a drop to be possible.
+		/// </remarks>
+		DragDropEffects Effects { get; set; }
+
+		/// <summary>
+		/// Gets the current insert position within <see cref="TargetCollection"/>.
+		/// </summary>
+		int InsertIndex { get; }
+
+		/// <summary>
+		/// Gets the collection that the target ItemsControl is bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the current drop target is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		IEnumerable TargetCollection { get; }
+
+		/// <summary>
+		/// Gets the object that the current drop target is bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the current drop target is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		object TargetItem { get; }
+
+		/// <summary>
+		/// Gets the current group target.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the drag is currently over an ItemsControl with groups, describes the group that
+		/// the drag is currently over.
+		/// </remarks>
+		CollectionViewGroup TargetGroup { get; }
+
+		/// <summary>
+		/// Gets the control that is the current drop target.
+		/// </summary>
+		UIElement VisualTarget { get; }
+
+		/// <summary>
+		/// Gets the item in an ItemsControl that is the current drop target.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the current drop target is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		UIElement VisualTargetItem { get; }
+
+		/// <summary>
+		/// Gets th orientation of the current drop target.
+		/// </summary>
+		Orientation VisualTargetOrientation { get; }
+	}
+
+	/// <summary>
+	/// Holds information about a the target of a drag drop operation.
+	/// </summary>
+	/// 
+	/// <remarks>
+	/// The <see cref="DropInfo"/> class holds all of the framework's information about the current 
+	/// target of a drag. It is used by <see cref="IDropTarget.DragOver"/> method to determine whether 
+	/// the current drop target is valid, and by <see cref="IDropTarget.Drop"/> to perform the drop.
+	/// </remarks>
+	public class DropInfo : IDropInfo
+	{
+		/// <summary>
+		/// Initializes a new instance of the DropInfo class.
+		/// </summary>
+		/// 
+		/// <param name="sender">
+		/// The sender of the drag event.
+		/// </param>
+		/// 
+		/// <param name="e">
+		/// The drag event.
+		/// </param>
+		/// 
+		/// <param name="dragInfo">
+		/// Information about the source of the drag, if the drag came from within the framework.
+		/// </param>
+		public DropInfo(object sender, DragEventArgs e, DragInfo dragInfo)
+		{
+			string dataFormat = DragDrop.DataFormat.Name;
+			Data = (e.Data.GetDataPresent(dataFormat)) ? e.Data.GetData(dataFormat) : e.Data;
+			DragInfo = dragInfo;
+
+			VisualTarget = sender as UIElement;
+			DropPosition = e.GetPosition(VisualTarget);
+
+			if (sender is ItemsControl)
+			{
+				ItemsControl itemsControl = (ItemsControl)sender;
+				UIElement item = itemsControl.GetItemContainerAt(DropPosition);
+				bool directlyOverItem = item != null;
+
+				TargetGroup = FindGroup(itemsControl, DropPosition);
+				VisualTargetOrientation = itemsControl.GetItemsPanelOrientation();
+
+				if (item == null)
+				{
+					item = itemsControl.GetItemContainerAt(DropPosition, VisualTargetOrientation);
+					directlyOverItem = false;
+				}
+
+				if (item != null)
+				{
+					ItemsControl itemParent = ItemsControl.ItemsControlFromItemContainer(item);
+
+					InsertIndex = itemParent.ItemContainerGenerator.IndexFromContainer(item);
+					TargetCollection = itemParent.ItemsSource ?? itemParent.Items;
+
+					if (directlyOverItem)
+					{
+						TargetItem = itemParent.ItemContainerGenerator.ItemFromContainer(item);
+						VisualTargetItem = item;
+					}
+
+					if (VisualTargetOrientation == Orientation.Vertical)
+					{
+						if (e.GetPosition(item).Y > item.RenderSize.Height / 2) InsertIndex++;
+					}
+					else
+					{
+						if (e.GetPosition(item).X > item.RenderSize.Width / 2) InsertIndex++;
+					}
+				}
+				else
+				{
+					TargetCollection = itemsControl.ItemsSource ?? itemsControl.Items;
+					InsertIndex = itemsControl.Items.Count;
+				}
+			}
+		}
+
+		private CollectionViewGroup FindGroup(ItemsControl itemsControl, Point position)
+		{
+			DependencyObject element = itemsControl.InputHitTest(position) as DependencyObject;
+
+			if (element != null)
+			{
+				GroupItem groupItem = element.GetVisualAncestor<GroupItem>();
+
+				if (groupItem != null)
+				{
+					return groupItem.Content as CollectionViewGroup;
+				}
+			}
+
+			return null;
+		}
+
+		/// <summary>
+		/// Gets the drag data.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the drag came from within the framework, this will hold:
+		/// 
+		/// - The dragged data if a single item was dragged.
+		/// - A typed IEnumerable if multiple items were dragged.
+		/// </remarks>
+		public object Data { get; private set; }
+
+		/// <summary>
+		/// Gets a <see cref="DragInfo"/> object holding information about the source of the drag, 
+		/// if the drag came from within the framework.
+		/// </summary>
+		public IDragInfo DragInfo { get; private set; }
+
+		/// <summary>
+		/// Gets the mouse position relative to the VisualTarget
+		/// </summary>
+		public Point DropPosition { get; private set; }
+
+		/// <summary>
+		/// Gets or sets the class of drop target to display.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// The standard drop target adorner classes are held in the <see cref="DropTargetAdorners"/>
+		/// class.
+		/// </remarks>
+		public Type DropTargetAdorner { get; set; }
+
+		/// <summary>
+		/// Gets or sets the allowed effects for the drop.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drop handler in order 
+		/// for a drop to be possible.
+		/// </remarks>
+		public DragDropEffects Effects { get; set; }
+
+		/// <summary>
+		/// Gets the current insert position within <see cref="TargetCollection"/>.
+		/// </summary>
+		public int InsertIndex { get; private set; }
+
+		/// <summary>
+		/// Gets the collection that the target ItemsControl is bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the current drop target is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		public IEnumerable TargetCollection { get; private set; }
+
+		/// <summary>
+		/// Gets the object that the current drop target is bound to.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the current drop target is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		public object TargetItem { get; private set; }
+
+		/// <summary>
+		/// Gets the current group target.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the drag is currently over an ItemsControl with groups, describes the group that
+		/// the drag is currently over.
+		/// </remarks>
+		public CollectionViewGroup TargetGroup { get; private set; }
+
+		/// <summary>
+		/// Gets the control that is the current drop target.
+		/// </summary>
+		public UIElement VisualTarget { get; private set; }
+
+		/// <summary>
+		/// Gets the item in an ItemsControl that is the current drop target.
+		/// </summary>
+		/// 
+		/// <remarks>
+		/// If the current drop target is unbound or not an ItemsControl, this will be null.
+		/// </remarks>
+		public UIElement VisualTargetItem { get; private set; }
+
+		/// <summary>
+		/// Gets th orientation of the current drop target.
+		/// </summary>
+		public Orientation VisualTargetOrientation { get; private set; }
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DropTargetAdorner.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Documents;
+using System.Windows;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	public abstract class DropTargetAdorner : Adorner
+	{
+		public DropTargetAdorner(UIElement adornedElement)
+			: base(adornedElement)
+		{
+			m_AdornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
+			m_AdornerLayer.Add(this);
+			IsHitTestVisible = false;
+		}
+
+		public void Detatch()
+		{
+			m_AdornerLayer.Remove(this);
+		}
+
+		public DropInfo DropInfo { get; set; }
+
+		internal static DropTargetAdorner Create(Type type, UIElement adornedElement)
+		{
+			if (!typeof(DropTargetAdorner).IsAssignableFrom(type))
+			{
+				throw new InvalidOperationException(
+					"The requested adorner class does not derive from DropTargetAdorner.");
+			}
+
+			return (DropTargetAdorner)type.GetConstructor(new[] { typeof(UIElement) })
+				.Invoke(new[] { adornedElement });
+		}
+
+		AdornerLayer m_AdornerLayer;
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DropTargetAdorners.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	public class DropTargetAdorners
+	{
+		public static Type Highlight
+		{
+			get { return typeof(DropTargetHighlightAdorner); }
+		}
+
+		public static Type Insert
+		{
+			get { return typeof(DropTargetInsertionAdorner); }
+		}
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DropTargetHighlightAdorner.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Media;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	public class DropTargetHighlightAdorner : DropTargetAdorner
+	{
+		public DropTargetHighlightAdorner(UIElement adornedElement)
+			: base(adornedElement)
+		{
+		}
+
+		protected override void OnRender(DrawingContext drawingContext)
+		{
+			if (DropInfo.VisualTargetItem != null)
+			{
+				Rect rect = new Rect(
+					DropInfo.VisualTargetItem.TranslatePoint(new Point(), AdornedElement),
+					VisualTreeHelper.GetDescendantBounds(DropInfo.VisualTargetItem).Size);
+				drawingContext.DrawRoundedRectangle(null, new Pen(Brushes.Gray, 2), rect, 2, 2);
+			}
+		}
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/DropTargetInsertionAdorner.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Controls;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	public class DropTargetInsertionAdorner : DropTargetAdorner
+	{
+		public DropTargetInsertionAdorner(UIElement adornedElement)
+			: base(adornedElement)
+		{
+		}
+
+		protected override void OnRender(DrawingContext drawingContext)
+		{
+			ItemsControl itemsControl = DropInfo.VisualTarget as ItemsControl;
+
+			if (itemsControl != null)
+			{
+				// Get the position of the item at the insertion index. If the insertion point is
+				// to be after the last item, then get the position of the last item and add an 
+				// offset later to draw it at the end of the list.
+				ItemsControl itemParent;
+
+				if (DropInfo.VisualTargetItem != null)
+				{
+					itemParent = ItemsControl.ItemsControlFromItemContainer(DropInfo.VisualTargetItem);
+				}
+				else
+				{
+					itemParent = itemsControl;
+				}
+
+				int index = Math.Min(DropInfo.InsertIndex, itemParent.Items.Count - 1);
+				UIElement itemContainer = (UIElement)itemParent.ItemContainerGenerator.ContainerFromIndex(index);
+
+				if (itemContainer != null)
+				{
+					Rect itemRect = new Rect(itemContainer.TranslatePoint(new Point(), AdornedElement),
+						itemContainer.RenderSize);
+					Point point1, point2;
+					double rotation = 0;
+
+					if (DropInfo.VisualTargetOrientation == Orientation.Vertical)
+					{
+						if (DropInfo.InsertIndex == itemParent.Items.Count)
+						{
+							itemRect.Y += itemContainer.RenderSize.Height;
+						}
+
+						point1 = new Point(itemRect.X, itemRect.Y);
+						point2 = new Point(itemRect.Right, itemRect.Y);
+					}
+					else
+					{
+						if (DropInfo.InsertIndex == itemParent.Items.Count)
+						{
+							itemRect.X += itemContainer.RenderSize.Width;
+						}
+
+						point1 = new Point(itemRect.X, itemRect.Y);
+						point2 = new Point(itemRect.X, itemRect.Bottom);
+						rotation = 90;
+					}
+
+					drawingContext.DrawLine(m_Pen, point1, point2);
+					DrawTriangle(drawingContext, point1, rotation);
+					DrawTriangle(drawingContext, point2, 180 + rotation);
+				}
+			}
+		}
+
+		void DrawTriangle(DrawingContext drawingContext, Point origin, double rotation)
+		{
+			drawingContext.PushTransform(new TranslateTransform(origin.X, origin.Y));
+			drawingContext.PushTransform(new RotateTransform(rotation));
+
+			drawingContext.DrawGeometry(m_Pen.Brush, null, m_Triangle);
+
+			drawingContext.Pop();
+			drawingContext.Pop();
+		}
+
+		static DropTargetInsertionAdorner()
+		{
+			// Create the pen and triangle in a static constructor and freeze them to improve performance.
+			const int triangleSize = 3;
+
+			m_Pen = new Pen(Brushes.Gray, 2);
+			m_Pen.Freeze();
+
+			LineSegment firstLine = new LineSegment(new Point(0, -triangleSize), false);
+			firstLine.Freeze();
+			LineSegment secondLine = new LineSegment(new Point(0, triangleSize), false);
+			secondLine.Freeze();
+
+			PathFigure figure = new PathFigure { StartPoint = new Point(triangleSize, 0) };
+			figure.Segments.Add(firstLine);
+			figure.Segments.Add(secondLine);
+			figure.Freeze();
+
+			m_Triangle = new PathGeometry();
+			m_Triangle.Figures.Add(figure);
+			m_Triangle.Freeze();
+		}
+
+		static Pen m_Pen;
+		static PathGeometry m_Triangle;
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/IDragSource.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	/// <summary>
+	/// Interface implemented by Drag Handlers.
+	/// </summary>
+	public interface IDragSource
+	{
+		/// <summary>
+		/// Queries whether a drag can be started.
+		/// </summary>
+		/// 
+		/// <param name="dragInfo">
+		/// Information about the drag.
+		/// </param>
+		/// 
+		/// <remarks>
+		/// To allow a drag to be started, the <see cref="DragInfo.Effects"/> property on <paramref name="dragInfo"/> 
+		/// should be set to a value other than <see cref="DragDropEffects.None"/>. 
+		/// </remarks>
+		void StartDrag(IDragInfo dragInfo);
+
+		/// <summary>
+		/// Notifies the drag handler that a drop has occurred.
+		/// </summary>
+		/// 
+		/// <param name="dropInfo">
+		///   Information about the drop.
+		/// </param>
+		void Dropped(IDropInfo dropInfo);
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Behaviors/DragAndDrop/IDropTarget.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Composer.UI.Behaviors.DragAndDrop
+{
+	/// <summary>
+	/// Interface implemented by Drop Handlers.
+	/// </summary>
+	public interface IDropTarget
+	{
+		/// <summary>
+		/// Updates the current drag state.
+		/// </summary>
+		/// 
+		/// <param name="dropInfo">
+		///   Information about the drag.
+		/// </param>
+		/// 
+		/// <remarks>
+		/// To allow a drop at the current drag position, the <see cref="DropInfo.Effects"/> property on 
+		/// <paramref name="dropInfo"/> should be set to a value other than <see cref="DragDropEffects.None"/>
+		/// and <see cref="DropInfo.Data"/> should be set to a non-null value.
+		/// </remarks>
+		void DragOver(IDropInfo dropInfo);
+
+		/// <summary>
+		/// Performs a drop.
+		/// </summary>
+		/// 
+		/// <param name="dropInfo">
+		///   Information about the drop.
+		/// </param>
+		void Drop(IDropInfo dropInfo);
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/ItemsControlExtensions.cs

+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Media;
+
+namespace Composer.UI
+{
+	public static class ItemsControlExtensions
+	{
+		public static bool CanSelectMultipleItems(this ItemsControl itemsControl)
+		{
+			if (itemsControl is MultiSelector)
+			{
+				// The CanSelectMultipleItems property is protected. Use reflection to
+				// get its value anyway.
+				return (bool)itemsControl.GetType()
+					.GetProperty("CanSelectMultipleItems", BindingFlags.Instance | BindingFlags.NonPublic)
+					.GetValue(itemsControl, null);
+			}
+			else if (itemsControl is ListBox)
+			{
+				return ((ListBox)itemsControl).SelectionMode != SelectionMode.Single;
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		public static UIElement GetItemContainer(this ItemsControl itemsControl, UIElement child)
+		{
+			Type itemType = GetItemContainerType(itemsControl);
+
+			if (itemType != null)
+			{
+				return (UIElement)child.GetVisualAncestor(itemType);
+			}
+
+			return null;
+		}
+
+		public static UIElement GetItemContainerAt(this ItemsControl itemsControl, Point position)
+		{
+			IInputElement inputElement = itemsControl.InputHitTest(position);
+			UIElement uiElement = inputElement as UIElement;
+
+			if (uiElement != null)
+			{
+				return GetItemContainer(itemsControl, uiElement);
+			}
+
+			return null;
+		}