Microsoft . 技术之路...

—— 专注于微软技术, 分享是快乐的源泉......
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

在WPF中如何实现列表控件的拖拽操作

Posted on 2006-04-27 10:02  赣江源  阅读(298)  评论(0编辑  收藏  举报

如果想要建立控件事件Drag & Drop 的结果, 从一个控件中的某一项拖动到另一个控件中,使用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>


后台代码 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;
  }


  
Adorner Class


后台代码 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.31New 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 IntegerAs 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 ObjectByVal 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 ObjectByVal 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 ObjectByVal 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 ObjectByVal 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 ObjectByVal 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 ObjectByVal 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