drag and drop between list boxes

Hi,

I'm just looking for a simple example of how to drap and drop an item from one listbox to another. I'm using datatemplates for both listboxes and i'd like to see the item in the preview when dragging.

** something that's compatible with the feb ctp 06 and in c# would be great too.

Thanks in advance.



Answer this question

drag and drop between list boxes

  • clarkiwrc

    the zipped files for the drag drop operations in C# are located here:

    http://blogs.msdn.com/llobo/archive/2006/04/11/573560.aspx


  • daat99

    Both VB.NET and C# Most Nifty. Thanks, Darius!
  • thomasn

    freeflyr, I have a series of posts about drag-drop over at http://blogs.msdn.com/marcelolr/.

    The first post is http://blogs.msdn.com/marcelolr/archive/2006/03/02/542641.aspx, and the preview adorner gets introduced in http://blogs.msdn.com/marcelolr/archive/2006/03/03/543301.aspx.

    I'll post the complete code in a couple of posts - there are still a couple of things I'd like to touch upon to build the sample out a bit more.


  • GMichaels

    In case anyone wants to just build the final result of the Drag & Drop series to date, here is the complete result of Marcelo's walkthroughs on drag and drop in VB.NET & C#.

    XAML - Window1.xaml
    ______________________________

    <Window x:Class="Window1"
    xmlns="
    http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="
    http://schemas.microsoft.com/winfx/2006/xaml"
    Title="SampleDragDrop" Height="300" Width="300"
    >
    <Window.Resources>
    <XmlDataProvider x:Key="MyData">
    <x:XData>
    <Sections xmlns="" Title="Library Favorites">
    <Section Name=".NET Development">
    <Article Name="Articles and Overview" />
    <Article Name=".NET Performance" />
    <Article Name="Windows Vista" />
    <Article Name="XML and the .NET Framework" />
    </Section>
    </Sections>
    </x:XData>
    </XmlDataProvider>
    <DataTemplate x:Key="ArticleTemplate">
    <TextBlock FontSize="10pt" Text="{Binding
    XPath=@Name}" />
    </DataTemplate>
    </Window.Resources>


    <DockPanel>

    <Border DockPanel.Dock="Top" BorderBrush="DarkGray" BorderThickness="2" Padding="4">
    <TextBlock FontSize="8pt" FontFamily="Tahoma" TextWrapping="Wrap">
    <Bold>
    Drag Sample
    </Bold>
    <LineBreak />
    <Run>
    This sample demonstrates using a DataObject for dragging "pure" data.
    </Run>
    </TextBlock>
    </Border>

    <ListBox Name="myListBox" AllowDrop="True"
    ItemsSource="{Binding Source={StaticResource MyData}, XPath=/Sections/Section/Article}"
    ItemTemplate="{StaticResource ArticleTemplate}">
    <ListBox.Background>
    <LinearGradientBrush>
    <LinearGradientBrush.GradientStops>
    <GradientStop Color="White" Offset="0" />
    <GradientStop Color="DarkBlue" Offset="1" />
    </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
    </ListBox.Background>
    </ListBox>

    </DockPanel>
    </Window>

    Code-Behind in C# - Window1.xaml.cs
    ____________________________________________

    public partial class Window1 : Window
    {
    private Point m_StartPoint;
    private bool m_IsDown;
    private System.Xml.XmlElement m_OriginalElement;
    private DropPreviewAdorner m_OverlayElement;
    private System.Xml.XmlElement m_RemoteElement;
    private static DataFormat m_MyFormat = DataFormats.GetDataFormat( "My Love-ly Format" );

    public Window1()
    {
    InitializeComponent();
    }

    protected override void OnInitialized( EventArgs e )
    {
    base.OnInitialized( e );

    myListBox.PreviewMouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler( MyCanvas_PreviewMouseLeftButtonDown );
    myListBox.PreviewMouseMove += new System.Windows.Input.MouseEventHandler( MyCanvas_PreviewMouseMove );
    myListBox.PreviewDragOver += new DragEventHandler( MyCanvas_PreviewDragOver );
    myListBox.PreviewDrop += new DragEventHandler( MyCanvas_PreviewDrop );
    myListBox.PreviewDragEnter += new DragEventHandler( MyCanvas_PreviewDragEnter );
    myListBox.PreviewDragLeave += new DragEventHandler( MyCanvas_PreviewDragLeave );
    }

    private void MyCanvas_PreviewMouseLeftButtonDown( object sender, System.Windows.Input.MouseButtonEventArgs e )
    {
    m_OriginalElement = GetElementFromPoint( myListBox, e.GetPosition( myListBox ) );
    if ( m_OriginalElement == null )
    {
    return;
    }
    m_IsDown = true;
    m_StartPoint = e.GetPosition( myListBox );
    }

    private System.Xml.XmlElement GetElementFromPoint( ListBox box, Point point )
    {
    UIElement element = (UIElement)box.InputHitTest( point );
    while ( true )
    {
    if ( element == box )
    {
    return null;
    }
    object item = box.ItemContainerGenerator.ItemFromContainer( element );
    bool itemFound = !( item.Equals( DependencyProperty.UnsetValue ) );
    if ( itemFound )
    {
    return item as System.Xml.XmlElement;
    }
    element = (UIElement)VisualTreeHelper.GetParent( element );
    }
    }

    private void MyCanvas_PreviewMouseMove( object sender, System.Windows.Input.MouseEventArgs e )
    {
    if ( m_IsDown )
    {
    if ( Math.Abs( e.GetPosition( myListBox ).X - m_StartPoint.X ) > SystemParameters.MinimumHorizontalDragDistance && Math.Abs( e.GetPosition( myListBox ).Y - m_StartPoint.Y ) > SystemParameters.MinimumVerticalDragDistance )
    {
    DragStarted();
    }
    }
    }

    private void DragStarted()
    {
    m_IsDown = false;
    string serializedObject = m_OriginalElement.OuterXml;
    DataObject data = new DataObject();
    data.SetData( m_MyFormat.Name, serializedObject );
    DragDropEffects effects = DragDrop.DoDragDrop( myListBox, data, DragDropEffects.Copy | DragDropEffects.Move );
    if ( effects == DragDropEffects.Move )
    {
    // Remove the element.
    m_OriginalElement.ParentNode.RemoveChild( m_OriginalElement );
    m_OriginalElement = null;
    }
    }

    private void DragMoved()
    {
    Point currentPosition = System.Windows.Input.Mouse.GetPosition( myListBox );
    m_OverlayElement.LeftOffset = currentPosition.X - m_StartPoint.X;
    m_OverlayElement.TopOffset = currentPosition.Y - m_StartPoint.Y;
    }

    private void MyCanvas_PreviewDragOver( object sender, System.Windows.DragEventArgs e )
    {
    if ( !( UpdateEffects( e ) ) )
    {
    return;
    }
    Point currentPosition = (Point) e.GetPosition( (IInputElement) this.Content );
    m_OverlayElement.LeftOffset = currentPosition.X;
    m_OverlayElement.TopOffset = currentPosition.Y;
    e.Handled = true;
    }

    private void MyCanvas_PreviewDrop( object sender, System.Windows.DragEventArgs e )
    {
    if ( !( UpdateEffects( e ) ) )
    {
    return;
    }
    XmlDataProvider dataProvider = (XmlDataProvider) FindResource( "MyData" );
    System.Xml.XmlDocument document = dataProvider.Document;
    System.Xml.XmlElement node = (System.Xml.XmlElement) dataProvider.Document.ImportNode( m_RemoteElement, true );
    dataProvider.Document.GetElementsByTagName( "Section" )[0].AppendChild(node);
    AdornerLayer.GetAdornerLayer( (System.Windows.Media.Visual) this.Content ).Remove( m_OverlayElement );
    m_RemoteElement = null;
    m_OverlayElement = null;
    e.Handled = true;
    }

    private void MyCanvas_PreviewDragEnter( object sender, System.Windows.DragEventArgs e )
    {
    if ( !( UpdateEffects( e ) ) )
    {
    return;
    }
    string serializedObject = (string) e.Data.GetData( m_MyFormat.Name );
    System.Xml.XmlDocument document = new System.Xml.XmlDocument();
    document.LoadXml( serializedObject );
    m_RemoteElement = document.DocumentElement;
    ContentPresenter presenter = new ContentPresenter();
    presenter.Content = m_RemoteElement;
    presenter.ContentTemplate = myListBox.ItemTemplate;
    AdornerLayer layer;
    m_OverlayElement = new DropPreviewAdorner( (UIElement)this.Content, presenter );
    layer = AdornerLayer.GetAdornerLayer( (System.Windows.Media.Visual) this.Content );
    layer.Add( m_OverlayElement );
    e.Handled = true;
    }

    private void MyCanvas_PreviewDragLeave( object sender, System.Windows.DragEventArgs e )
    {
    if ( m_OverlayElement == null )
    {
    return;
    }
    AdornerLayer.GetAdornerLayer( ( System.Windows.Media.Visual ) this.Content ).Remove( m_OverlayElement );
    m_OverlayElement = null;
    m_RemoteElement = null;
    e.Handled = true;
    }

    private bool UpdateEffects( System.Windows.DragEventArgs e )
    {
    if ( !( e.Data.GetDataPresent( m_MyFormat.Name ) ) )
    {
    e.Effects = DragDropEffects.None;
    return false;
    }
    if ( ( e.AllowedEffects & DragDropEffects.Copy ) == 0 && ( e.AllowedEffects & DragDropEffects.Move ) == 0 )
    {
    e.Effects = DragDropEffects.None;
    return false;
    }
    if ( ( e.AllowedEffects & DragDropEffects.Copy ) != 0 && ( e.AllowedEffects & DragDropEffects.Move ) != 0 )
    {
    if ( ( e.KeyStates & DragDropKeyStates.ControlKey ) != 0 )
    {
    e.Effects = DragDropEffects.Copy;
    }
    else
    {
    e.Effects = DragDropEffects.Move;
    }
    }
    else
    {
    e.Effects = e.AllowedEffects & (( DragDropEffects.Copy | DragDropEffects.Move ) );
    }
    return true;
    }

    #region Adorner Class
    class DropPreviewAdorner : Adorner
    {

    public DropPreviewAdorner( UIElement adornedElement, UIElement adorningElement )
    : base( adornedElement )
    {
    VisualBrush brush = new VisualBrush( adorningElement );
    m_Child = new Rectangle();
    m_Child.Width = adorningElement.RenderSize.Width;
    m_Child.Height = adorningElement.RenderSize.Height;
    m_Child.Fill = brush;
    m_Child.IsHitTestVisible = false;
    System.Windows.Media.Animation.DoubleAnimation animation;
    animation = new System.Windows.Media.Animation.DoubleAnimation( 0.3, 1, new Duration( TimeSpan.FromSeconds( 1 ) ) );
    animation.AutoReverse = true;
    animation.RepeatBehavior = System.Windows.Media.Animation.RepeatBehavior.Forever;
    brush.BeginAnimation( System.Windows.Media.Brush.OpacityProperty, animation );
    }
    private Rectangle m_Child;
    private double m_LeftOffset;
    private double m_TopOffset;

    protected override System.Windows.Size MeasureOverride( System.Windows.Size constraint )
    {
    m_Child.Measure( constraint );
    return m_Child.DesiredSize;
    }

    protected override System.Windows.Size ArrangeOverride( System.Windows.Size finalSize )
    {
    m_Child.Arrange( new Rect( finalSize ) );
    return finalSize;
    }

    protected override System.Windows.Media.Visual GetVisualChild( int index )
    {
    return m_Child;
    }

    protected override int VisualChildrenCount
    {
    get
    {
    return 1;
    }
    }

    public double LeftOffset
    {
    get
    {
    return m_LeftOffset;
    }
    set
    {
    m_LeftOffset = value;
    UpdatePosition();
    }
    }

    public double TopOffset
    {
    get
    {
    return m_TopOffset;
    }
    set
    {
    m_TopOffset = value;
    UpdatePosition();
    }
    }

    private void UpdatePosition()
    {
    AdornerLayer adornerLayer = this.Parent as AdornerLayer;
    if ( adornerLayer != null )
    {
    adornerLayer.Update( AdornedElement );
    }
    }

    public override System.Windows.Media.GeneralTransform GetDesiredTransform( System.Windows.Media.GeneralTransform transform )
    {
    GeneralTransformGroup result = new GeneralTransformGroup();
    result.Children.Add( base.GetDesiredTransform( transform ) );
    result.Children.Add( new TranslateTransform( LeftOffset, TopOffset ) );
    return result;
    }
    }
    #endregion

    Code-Behind in VB.NET - Window1.xaml.vb
    ______________________________________________

    Partial Public Class Window1
    Inherits Window

    Class DropPreviewAdorner
    Inherits Adorner

    Public Sub New(ByVal adornedElement As UIElement, _
    ByVal adorningElement As UIElement)
    MyBase.New(adornedElement)

    Dim brush As VisualBrush = New VisualBrush(adorningElement)

    m_Child = New Rectangle()
    m_Child.Width = adorningElement.RenderSize.Width
    m_Child.Height = adorningElement.RenderSize.Height
    m_Child.Fill = brush
    m_Child.IsHitTestVisible = False

    Dim animation As System.Windows.Media.Animation.DoubleAnimation
    animation = New System.Windows.Media.Animation.DoubleAnimation(0.3, 1, New Duration(TimeSpan.FromSeconds(1)))
    animation.AutoReverse = True
    animation.RepeatBehavior = System.Windows.Media.Animation.RepeatBehavior.Forever
    brush.BeginAnimation(System.Windows.Media.Brush.OpacityProperty, animation)
    End Sub

    ' Adding some basic fields to help us keep track of where we are and what we render
    Private m_Child As Rectangle
    Private m_LeftOffset As Double
    Private m_TopOffset As Double

    Protected Overrides Function MeasureOverride(ByVal constraint As System.Windows.Size) As System.Windows.Size
    m_Child.Measure(constraint)
    Return m_Child.DesiredSize
    End Function

    Protected Overrides Function ArrangeOverride(ByVal finalSize As System.Windows.Size) As System.Windows.Size
    m_Child.Arrange(New Rect(finalSize))
    Return finalSize
    End Function

    Protected Overrides Function GetVisualChild(ByVal index As Integer) As System.Windows.Media.Visual
    Return m_Child
    End Function

    Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
    Get
    Return 1
    End Get
    End Property

    Public Property LeftOffset() As Double
    Get
    Return m_LeftOffset
    End Get
    Set(ByVal value As Double)
    m_LeftOffset = value
    UpdatePosition()
    End Set
    End Property

    Public Property TopOffset() As Double
    Get
    Return m_TopOffset
    End Get
    Set(ByVal value As Double)
    m_TopOffset = value
    UpdatePosition()
    End Set
    End Property

    Private Sub UpdatePosition()
    Dim adornerLayer As AdornerLayer = Me.Parent
    If Not adornerLayer Is Nothing Then
    adornerLayer.Update(AdornedElement)
    End If
    End Sub

    Public Overrides Function GetDesiredTransform(ByVal transform As System.Windows.Media.GeneralTransform) As System.Windows.Media.GeneralTransform
    Dim result As GeneralTransformGroup = New GeneralTransformGroup()
    result.Children.Add(MyBase.GetDesiredTransform(transform))
    result.Children.Add(New TranslateTransform(LeftOffset, TopOffset))
    Return result
    End Function

    End Class

    Private m_StartPoint As Point ' Where did the mouse start off from
    Private m_IsDown As Boolean ' Is the mouse down right now
    Private m_OriginalElement As System.Xml.XmlElement ' What is it that we're dragging
    Private m_OverlayElement As DropPreviewAdorner ' What is it that we're using to show
    Private m_RemoteElement As System.Xml.XmlElement
    Private Shared m_MyFormat As DataFormat = DataFormats.GetDataFormat("My Love-ly Format")

    Public Sub New()
    InitializeComponent()
    End Sub

    Private Sub myListBox_PreviewMouseLeftButtonDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles myListBox.PreviewMouseLeftButtonDown
    m_OriginalElement = GetElementFromPoint(myListBox, e.GetPosition(myListBox))
    If m_OriginalElement Is Nothing Then Exit Sub
    m_IsDown = True
    m_StartPoint = e.GetPosition(myListBox)
    End Sub

    Private Function GetElementFromPoint(ByVal box As ListBox, ByVal point As Point) As System.Xml.XmlElement
    Dim element As UIElement = box.InputHitTest(point)
    While True
    If element Is box Then Return Nothing
    Dim item As Object = box.ItemContainerGenerator.ItemFromContainer(element)
    Dim itemFound As Boolean = Not item.Equals(DependencyProperty.UnsetValue)
    If itemFound Then Return item
    element = VisualTreeHelper.GetParent(element)
    End While
    Return Nothing
    End Function

    Private Sub myListBox_PreviewMouseMove(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles myListBox.PreviewMouseMove
    If m_IsDown Then
    If Math.Abs(e.GetPosition(myListBox).X - m_StartPoint.X) > SystemParameters.MinimumHorizontalDragDistance AndAlso _
    Math.Abs(e.GetPosition(myListBox).Y - m_StartPoint.Y) > SystemParameters.MinimumVerticalDragDistance Then
    DragStarted()
    End If
    End If
    End Sub

    Private Sub DragStarted()
    m_IsDown = False

    Dim serializedObject As String = m_OriginalElement.OuterXml
    Dim data As DataObject = New DataObject()
    data.SetData(m_MyFormat.Name, serializedObject)
    Dim effects As DragDropEffects = _
    DragDrop.DoDragDrop(myListBox, data, DragDropEffects.Copy Or DragDropEffects.Move)
    If effects And DragDropEffects.Move Then
    ' Remove the element.
    m_OriginalElement.ParentNode.RemoveChild(m_OriginalElement)
    m_OriginalElement = Nothing
    End If
    End Sub

    Private Sub DragMoved()
    Dim currentPosition As Point = System.Windows.Input.Mouse.GetPosition(myListBox)
    m_OverlayElement.LeftOffset = currentPosition.X - m_StartPoint.X
    m_OverlayElement.TopOffset = currentPosition.Y - m_StartPoint.Y
    End Sub

    Private Sub myListBox_PreviewDragOver(ByVal sender As Object, ByVal e As System.Windows.DragEventArgs) Handles myListBox.PreviewDragOver
    If Not UpdateEffects(e) Then
    Exit Sub
    End If

    Dim currentPosition As Point = e.GetPosition(Me.Content)
    m_OverlayElement.LeftOffset = currentPosition.X
    m_OverlayElement.TopOffset = currentPosition.Y

    e.Handled = True
    End Sub

    Private Sub myListBox_PreviewDrop(ByVal sender As Object, ByVal e As System.Windows.DragEventArgs) Handles myListBox.PreviewDrop
    If Not UpdateEffects(e) Then
    Exit Sub
    End If

    ' Add the element.
    Dim dataProvider As XmlDataProvider = FindResource("MyData")
    Dim document As System.Xml.XmlDocument = dataProvider.Document
    Dim node As System.Xml.XmlElement = dataProvider.Document.ImportNode(m_RemoteElement, True)
    dataProvider.Document.GetElementsByTagName("Section")(0).AppendChild(node)

    ' Cleanup
    AdornerLayer.GetAdornerLayer(Me.Content).Remove(m_OverlayElement)
    m_RemoteElement = Nothing
    m_OverlayElement = Nothing

    e.Handled = True
    End Sub

    Private Sub myListBox_PreviewDragEnter(ByVal sender As Object, ByVal e As System.Windows.DragEventArgs) Handles myListBox.PreviewDragEnter
    If Not UpdateEffects(e) Then
    Exit Sub
    End If

    ' First, we deserialize the object provided to us.
    Dim serializedObject As String = e.Data.GetData(m_MyFormat.Name)
    Dim document As System.Xml.XmlDocument = New System.Xml.XmlDocument()
    document.LoadXml(serializedObject)
    m_RemoteElement = document.DocumentElement

    ' Now, create something we can render with.
    Dim presenter As ContentPresenter = New ContentPresenter()
    presenter.Content = m_RemoteElement
    presenter.ContentTemplate = myListBox.ItemTemplate

    ' Next, create an adorner for it.
    Dim layer As AdornerLayer
    m_OverlayElement = New DropPreviewAdorner(Me.Content, presenter)
    layer = AdornerLayer.GetAdornerLayer(Me.Content)
    layer.Add(m_OverlayElement)

    e.Handled = True
    End Sub

    Private Sub myListBox_PreviewDragLeave(ByVal sender As Object, ByVal e As System.Windows.DragEventArgs) Handles myListBox.PreviewDragLeave
    If m_OverlayElement Is Nothing Then Exit Sub

    AdornerLayer.GetAdornerLayer(Me.Content).Remove(m_OverlayElement)
    m_OverlayElement = Nothing
    m_RemoteElement = Nothing

    e.Handled = True
    End Sub

    Private Function UpdateEffects(ByVal e As System.Windows.DragEventArgs) As Boolean
    ' If we don't know what we're talking about, we shouldn't do anything.
    If Not e.Data.GetDataPresent(m_MyFormat.Name) Then
    e.Effects = DragDropEffects.None
    Return False
    End If

    ' If we can't copy or move, we shouldn't do anything (eg: provider wants us to link).
    If (e.AllowedEffects And DragDropEffects.Copy) = 0 AndAlso _
    (e.AllowedEffects And DragDropEffects.Move) = 0 Then
    e.Effects = DragDropEffects.None
    Return False
    End If

    ' Figure out whether we should copy or move. If we can do either, we'll move unless
    ' Ctrl is pressed.
    If (e.AllowedEffects And DragDropEffects.Copy) <> 0 AndAlso _
    (e.AllowedEffects And DragDropEffects.Move) <> 0 Then
    If (e.KeyStates And DragDropKeyStates.ControlKey) <> 0 Then
    e.Effects = DragDropEffects.Copy
    Else
    e.Effects = DragDropEffects.Move
    End If
    Else
    e.Effects = e.AllowedEffects And Not (DragDropEffects.Copy Or DragDropEffects.Move)
    End If
    Return True
    End Function

    End Class


  • Drak Swordsman

    You might want to take a look at Mercelo's blog, there are at least 2 or 3 examples of drag and drop, the latest particularly looks alot like what your looking for.

    http://blogs.msdn.com/marcelolr/archive/category/11786.aspx


  • HowardCarr

    Thanks marcelo, i look forward to it.
  • Hal T

    Thanks for the response but marcelo's example seems to be incomplete, i tried to reconstruct it but he declares a variable of type DropPreviewAdorner which does not exist as far as i can see... as i mentioned i am running the feb ctp so perhaps that's the reason but i'd still like to see some code that works on it if anyone has it.


  • drag and drop between list boxes