
It's not much fun to implement DragDrop in WPF by yourself, but it's all possible of course, and the problem with using libraries is always limitations at some point. I've attached a sample that shows how to link your behavior to the viewmodel. You need to manually initiate DoDragDrop, as noted.
What is not included in the sample: drop logic, which tells you what happens on the drop (reorder items if DragDrop was inside same listbox, add if it was outside). If you want a slightly more complex and complete sample, have a look at my answer here.
Xaml:
<UserControl x:Class="WpfApplication1.Controls.DragOverDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
xmlns:beh="clr-namespace:WpfApplication1.Behavior"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<UserControl.DataContext>
<vm:DragOverDemoViewModel />
</UserControl.DataContext>
<StackPanel x:Name="MainPanel">
<TextBlock Text="{Binding State}" Margin="5" />
<ListBox x:Name="icTodoList" Background="#FFF3800C" Canvas.Top="25" Height="600" BorderThickness="0" BorderBrush="{x:Null}">
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#FFF3800C" Opacity="0.2"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="#FFF3800C" />
</ListBox.Resources>
<ListBox x:Name="InnerListBox" ItemsSource="{Binding Items}" Margin="0" Height="550" Width="250" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<i:Interaction.Behaviors>
<beh:DragDropBehavior DragOverCommand="{Binding DragOverCommand}" DropCommand="{Binding DropCommand}" />
</i:Interaction.Behaviors>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border MinHeight="30" MinWidth="100" BorderThickness="1" BorderBrush="DarkGray" Margin="2" >
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ListBox>
</StackPanel>
</UserControl>
DragDropBehavior:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace WpfApplication1.Behavior
{
public class DragDropBehavior : Behavior<ItemsControl>
{
private bool _isDragging;
private IDataObject _dataObject;
private Type _dataType;
public ICommand DragOverCommand { get { return (ICommand)GetValue(DragOverCommandProperty); } set { SetValue(DragOverCommandProperty, value); } }
public static readonly DependencyProperty DragOverCommandProperty = DependencyProperty.Register("DragOverCommand", typeof(ICommand), typeof(DragDropBehavior), new PropertyMetadata(null));
public ICommand DropCommand { get { return (ICommand)GetValue(DropCommandProperty); } set { SetValue(DropCommandProperty, value); } }
public static readonly DependencyProperty DropCommandProperty = DependencyProperty.Register("DropCommand", typeof(ICommand), typeof(DragDropBehavior), new PropertyMetadata(null));
protected override void OnAttached()
{
this.AssociatedObject.AllowDrop = true;
this.AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
this.AssociatedObject.PreviewMouseMove += AssociatedObject_PreviewMouseMove;
this.AssociatedObject.PreviewMouseLeftButtonUp += AssociatedObject_PreviewMouseLeftButtonUp;
base.OnAttached();
}
void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// get data from mouse position
ItemsControl itemsControl = (ItemsControl)sender;
Point p = e.GetPosition(itemsControl);
object data = itemsControl.GetDataObjectFromPoint(p);
if (data != null)
{
_dataType = data.GetType();
_dataObject = new DataObject(_dataType, data);
_isDragging = true; // valid data found, set dragging to true
}
}
void AssociatedObject_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_isDragging)
{
// let viewmodel know of the drag
this.DragOverCommand.Execute(_dataObject.GetData(_dataType));
// execute drag
DragDropEffects eff = DragDrop.DoDragDrop(this.AssociatedObject, _dataObject, DragDropEffects.Copy | DragDropEffects.Move);
// thread waits for DragDrop to finish...
Drop();
}
}
void AssociatedObject_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_isDragging = false;
}
private void Drop()
{
// let viewmodel know of the drop
this.DropCommand.Execute(_dataObject.GetData(_dataType));
_isDragging = false;
}
}
}
ViewModel:
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1.ViewModels
{
public class DragOverDemoViewModel : NotifyBase
{
public class ItemModel : NotifyBase
{
public string Name { get { return base.GetValue(() => Name); } set { base.SetValue(() => Name, value); } }
}
public ObservableCollection<ItemModel> Items { get; private set; }
public ICommand DragOverCommand { get { return base.GetValue(() => DragOverCommand); } set { base.SetValue(() => DragOverCommand, value); } }
public ICommand DropCommand { get { return base.GetValue(() => DropCommand); } set { base.SetValue(() => DropCommand, value); } }
public string State { get { return base.GetValue(() => State); } set { base.SetValue(() => State, value); } }
public DragOverDemoViewModel()
{
this.Items = new ObservableCollection<ItemModel>()
{
new ItemModel() { Name = "Item 1"},
new ItemModel() { Name = "Item 2"},
new ItemModel() { Name = "Item 3"},
new ItemModel() { Name = "Item 4"},
new ItemModel() { Name = "Item 5"},
new ItemModel() { Name = "Item 6"},
new ItemModel() { Name = "Item 7"},
};
this.DragOverCommand = new ActionCommand<ItemModel>(DragOver);
this.DropCommand = new ActionCommand<ItemModel>(Drop);
}
private void DragOver(ItemModel item) { this.State = string.Format("Dragging {0}...", item.Name); }
private void Drop(ItemModel item) { this.State = string.Format("Dropped {0}.", item.Name); }
}
public class ActionCommand<T> : ICommand
{
public event EventHandler CanExecuteChanged;
private Action<T> _action;
public ActionCommand(Action<T> action)
{
_action = action;
}
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter)
{
if (_action != null)
{
var castParameter = (T)Convert.ChangeType(parameter, typeof(T));
_action(castParameter);
}
}
}
}