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

7
我已经使用Bea的解决方案(这里)有一段时间了,发现它非常有帮助。但现在遇到的问题是,当我在ListView控件内部或将项目拖放到另一个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”的变量,它的类型是Picture(在Bea的示例中),但这取决于你绑定到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在拖动项目时滚动吗?

谢谢!


抱歉,我还没有时间进一步研究这个问题。自你发布以来,你有遇到什么新情况吗? - ScottN
3个回答

2

我还在处理这个问题。我正在使用稍微修改过的Bea's Drag and Drop,它是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

我必须包含这个实用函数,它取自这里

    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

我终于实现了这个功能并快速测试了一下。看起来很不错!运行得非常好!谢谢! - ScottN
我发现了一个问题,我在列表框上使用双击作为在两个控件之间拖动项目的替代方法。但是,使用上述方法时,它会出现检测单击和双击之间的问题,源列表将添加回我刚刚删除的项目。因此,在鼠标按下函数中,我添加了"If e.Clicks = 1 Then Do Drag Else Set DraggedData = Nothing",这应该可以解决问题。 - ScottN

2
我所做的是利用ListBox.ScrollIntoView方法。基本上,当你更新你的拖放目标时,你可以在它上面调用这个方法,wpf会处理所有的工作。你只需要知道拖放目标项的索引即可。这可以处理垂直和水平滚动。 this.listView.ScrollIntoView(this.listView.Items[index]); 当你使用这个方法时,你的装饰器可能会随着滚动ListBox而移动。为了解决这个问题,我只需将我的装饰器父级和装饰器层父级设置为视觉树顶部窗口的内容(即topWindow.Content)。

1
我会尝试一下,感谢您在三个月后发布 :) 我以为这被遗忘了.. - ScottN
lstMessages.ScrollIntoView(lstMessages.Items[lstMessages.Items.Count-1]); 感谢!我做得很相似。 - Sebastian Castaldi

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滚动一定数量,您可以通过设置ScrollBar的“SmallAmount”属性来配置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接