WPF ListView数据绑定拖放自动滚动

时间:2009-02-18 02:20:30

标签: wpf data-binding listview collections drag-and-drop

我一直在使用Bea的解决方案here一段时间,并发现它非常有用。现在我遇到的问题是,当我将项目拖放到另一个ListView控件中时,我想在拖动期间向上/向下滚动(将项目从索引30移动到索引1),它不会发生。我必须拖动到ListView中的可视项目的顶部,手动向上滚动,然后再次拖动,最终在我想要的位置结束。这不是非常用户友好。

现在我找到了一个函数(DragDropHelper.DropTarget_PreviewDragOver),我想要测试哪个项目被拖过来,我就是这样。

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)

现在我可以从那里获得此视觉命中的DependencyProperty

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

哪个是ListViewItem。现在在函数DropTarget_PreviewDragOver中,我有“DraggedItem”,它在Bea的示例中是Picture类型,但是这可以根据你绑定到ListView的ObservableCollection而改变。现在,我想根据鼠标在控件上的位置向上或向下拖动ListView。我尝试使用下面未完成的非工作代码

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

有人能指出我正确的方向让这个ItemsControl或ListView在拖动项目时滚动吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

我所做的是利用了ListBox.ScrollIntoView方法。基本上,当您更新放置目标时,您可以在其上调用此方法,并且wpf将完成所有工作。您需要知道的只是放置目标项的索引。这可以处理垂直和水平滚动。

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

使用此方法时,您的装饰器可能会随着滚动ListBox一起移动。要解决此问题,我只需将我的装饰父级和装配体图层父级设置为可视树顶部窗口的内容(即topWindow.Content)。

答案 1 :(得分:2)

我仍然在解决同样的问题。我正在使用稍微修改过的Bea的Drag and Drop版本here,它在VB而不是C#中。当我如上所述使用ScrollIntoView时,我可以向下滚动但不能向上滚动。所以我搞砸了,想出了我的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

我必须包含此实用程序功能,取自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

这是伟大的。然后在装饰者移动的情况下用尽上面提到的theuberk,并且为了让其他人以后容易这样做,我在DragDropAdorner类中添加了一个变量:

    Private m_mouseDelta As Point

将此添加到DragSource_PreviewMouseLeftButtonDown的最后一行:

        Me.m_mouseDelta = e.GetPosition(m_sourceItemContainer)

将ShowDraggedAdorner变为:

    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

答案 2 :(得分:0)

滚动的另一种可能性是使用ScrollBar-Commands。您可以在不降低VisualTree的情况下执行此操作。如果你有一个没有边框的样式ListBox,GetScrollViewer() - 方法就不再有用了。

我使用ItemContainerGenerator的第一个元素作为ScrollBar.LineXXXCommand的CommandTarget:

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);
  }

调用LineXXXCommands类似于单击ScrollBar的箭头按钮:ScrollViewer按某个ammount滚动,您可以通过设置ScrollBar的'SmallAmount'属性来配置。