2009-02-18 48 views
7

He estado trabajando con la solución de Bea here por un tiempo y me resulta muy útil. El problema ahora es que cuando arrastro y solto elementos dentro de o hacia otro control ListView y deseo desplazarme hacia arriba/abajo "durante" el arrastre (moviendo un elemento del índice 30 al índice 1), no está sucediendo. Tendría que arrastrar hasta la parte superior de los elementos visuales en ListView, desplazarme manualmente hacia arriba, luego arrastrar de nuevo, y finalmente terminar en la posición que quiero. Esto no es muy amigable para el usuario.WPF ListView Databound Drag/Drop Auto Scroll

Ahora encontré la función (DragDropHelper.DropTarget_PreviewDragOver) que me gustaría hacer la prueba de qué elemento se está arrastrando, y lo estoy obteniendo.

Dim pt As Point = e.GetPosition(DirectCast(Me.targetItemsControl, UIElement)) 

' Perform the hit test against a given portion of the visual object tree. 
Dim result As HitTestResult = VisualTreeHelper.HitTest(Me.targetItemsControl, pt) 

Ahora desde allí se puede obtener la propiedad de dependencia de este golpe visual

Dim lvi As ListViewItem = TryCast(GetDependencyObjectFromVisualTree(TryCast(result.VisualHit, DependencyObject), GetType(ListViewItem)), ListViewItem) 

que es de un ListViewItem. Ahora en la función DropTarget_PreviewDragOver tengo el "DraggedItem" que es de tipo Picture en el ejemplo de Bea, pero eso puede cambiar dependiendo de la ObservableCollection que haya enlazado al ListView. Ahora, quiero arrastrar el ListView hacia arriba o hacia abajo dependiendo de dónde esté el mouse en el control. He tratado con el siguiente sin finalizar código que no funciona

If lvi IsNot Nothing Then 
    If pt.Y <= 25 Then 
     Dim lv As ListView = TryCast(targetItemsControl, ListView) 
     If lv IsNot Nothing Then 
      Dim index As Integer = lv.Items.IndexOf(lvi) 
      If index > 1 Then 
       lv.ScrollIntoView(lv.Items(index - 1)) 
      End If 
     End If 
    Else 
     If pt.Y >= Me.targetItemsControl.ActualHeight - 25 Then 
      Debug.Print("Scroll Down") 
     End If 
    End If 
End If 

Alguien me puede apuntar en la dirección correcta para conseguir este ItemsControl o ListView para desplazarse al arrastrar sobre los artículos ??

Gracias!

+0

Hola, ¿Encontró una solución? Cheers –

+0

Lo siento, todavía no he tenido tiempo de investigar esto más. ¿Has encontrado algo desde que publicaste? – ScottN

Respuesta

2

Todavía estoy jugando con este mismo problema también. Estoy usando una versión ligeramente modificada de Bea's Drag and Drop encontrado here, que está en VB en lugar de C#. Cuando utilicé ScrollIntoView como se describe arriba, pude desplazarme hacia abajo pero no hacia arriba. Así que cambiar sus planes y se acercó con esto como mi DropTarget_PreviewDragOver:

Private Sub DropTarget_PreviewDragOver(ByVal sender As Object, ByVal e As DragEventArgs) 
     Dim draggedItem As Object = e.Data.GetData(Me.m_format.Name) 
     Me.DecideDropTarget(e) 
     If (Not draggedItem Is Nothing) Then 
      If (TypeOf m_targetItemsControl Is ListBox) Then 
       Dim lb As ListBox = CType(m_targetItemsControl, ListBox) 
       Dim temp As Integer = m_insertionIndex 
       Dim scroll As ScrollViewer = Utilities.GetScrollViewer(lb) 
       If scroll.VerticalOffset = temp Then 
        temp -= 1 
       End If 
       If temp >= 0 And temp <= (lb.Items.Count - 1) Then 
        lb.ScrollIntoView(lb.Items(temp)) 
       End If 
      End If 
      Me.ShowDraggedAdorner(e.GetPosition(Me.m_topWindow)) 
      Me.UpdateInsertionAdornerPosition() 
     End If 
     e.Handled = True 
    End Sub 

y tuve que incluya esta función de utilidad, tomada de here

Public Shared Function GetScrollViewer(ByVal listBox As ListBox) 
    Dim scroll_border As Decorator = CType(VisualTreeHelper.GetChild(listBox, 0), Decorator) 
    If (TypeOf scroll_border Is Decorator) Then 
     Dim scroll As ScrollViewer = CType(scroll_border.Child, ScrollViewer) 
     If (TypeOf scroll Is ScrollViewer) Then 
      Return scroll 
     Else 
      Return Nothing 
     End If 
    Else 
     Return Nothing 
    End If 


End Function 

que es grande y todo.A continuación, acabando lo theuberk mencionado anteriormente con el movimiento adorner, y en el espíritu de hacer esto fácil para alguien más tarde, he añadido una variable a la clase DragDropAdorner:

Private m_mouseDelta As Point 

añadido esto a la última línea de DragSource_PreviewMouseLeftButtonDown:

 Me.m_mouseDelta = e.GetPosition(m_sourceItemContainer) 

y se volvió ShowDraggedAdorner en:

Private Sub ShowDraggedAdorner(ByVal currentPosition As Point) 
    If (Me.m_draggedAdorner Is Nothing) Then 
     Dim adornerLayer As AdornerLayer = adornerLayer.GetAdornerLayer(Me.m_topWindow.Content) 
     Me.m_draggedAdorner = New DraggedAdorner(Me.m_draggedData, DragDropBehavior.GetDragTemplate(Me.m_sourceItemsControl), m_topWindow.Content, adornerLayer) 
    End If 
    Me.m_draggedAdorner.SetPosition((currentPosition.X - m_mouseDelta.X), (currentPosition.Y - m_mouseDelta.Y)) 
    End Sub 
+0

Finalmente llegué a implementar esto y probarlo realmente rápido. ¡Se ve muy bien! Funcionó bien! ¡Gracias! – ScottN

+0

Sí encontré un problema, estaba haciendo doble clic en el cuadro de lista como alternativa al arrastre de elementos entre dos controles. Al usar el método anterior, tuve problemas para detectar un solo clic entre un doble clic y en la lista de fuentes se agregaría el elemento que acabo de eliminar. Entonces en la función del mouse agregué "If e.Clicks = 1 Then Do Drag Else Set DraggedData = Nothing", eso debería arreglarlo. – ScottN

2

Lo que hice fue aprovechar el método ListBox.ScrollIntoView. Básicamente, cuando actualiza su destino de caída, puede simplemente llamar a este método y wpf hará todo el trabajo. Todo lo que necesita saber es el índice del elemento de destino de caída. Esto maneja el desplazamiento vertical y horizontal.

this.listView.ScrollIntoView(this.listView.Items[index]);

Cuando se utiliza este método, su adorner podría moverse con el cuadro de lista de desplazamiento. Para solucionar esto, simplemente configuré mi elemento primario adornador y el elemento primario de la capa adorner en el contenido de la ventana en la parte superior del árbol visual (es decir, topWindow.Content).

+1

Voy a intentarlo, gracias por publicar 3 meses después :) Pensé que se había olvidado ... – ScottN

+0

gracias! Lo hice similar lstMessages.ScrollIntoView (lstMessages.Items [lstMessages.Items.Count-1]); –

0

Otra posibilidad para desplazarse es lo utilicen-Comandos. Puede hacer esto sin bajar por el VisualTree. Si tiene un ListBox de estilos sin borde, GetScrollViewer() - Method ya no funcionaría.

uso el primer elemento de la ItemContainerGenerator como CommandTarget para la ScrollBar.LineXXXCommand:

Point p = e.GetPosition(itemsControl); 
    IInputElement commandTarget = itemsControl.ItemContainerGenerator.ContainerFromIndex(0) as IInputElement; 

    if (commandTarget != null) 
    { 
    if (p.Y < OFFSET_TO_SCROLL) 
     ScrollBar.LineUpCommand.Execute(null, commandTarget); 
    else if (p.Y > itemsControl.ActualHeight - OFFSET_TO_SCROLL) 
     ScrollBar.LineDownCommand.Execute(null, commandTarget); 

    if (p.X < OFFSET_TO_SCROLL) 
     ScrollBar.LineLeftCommand.Execute(null, commandTarget); 
    else if (p.X > itemsControl.ActualWidth - OFFSET_TO_SCROLL) 
     ScrollBar.LineRightCommand.Execute(null, commandTarget); 
    } 

Llamar a los LineXXXCommands es similar a clic en la flecha-botones de una barra de desplazamiento: Los scrolles ScrollViewer por cierto, que ammount puede configurar estableciendo la propiedad 'SmallAmount' de ScrollBar.

Cuestiones relacionadas