I\'ve been working with Bea\'s solution here for a while and finding it very helpful. Problem now I\'m having is when I drag-n-drop items within or to another ListView control
Another possibility to scroll is to use the ScrollBar-Commands. You can do this without climbing down the VisualTree. If you have a styles ListBox with no border the GetScrollViewer()-Method would not work anymore.
I use the first Element of the ItemContainerGenerator as CommandTarget for the 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);
}
Calling the LineXXXCommands is similiar to clicking the Arrow-Buttons of a ScrollBar: The ScrollViewer scrolles by a certain ammount which you can configure by setting the 'SmallAmount' Property of the ScrollBar.
What I did was took advantage of the ListBox.ScrollIntoView
method. Basically, when you update your drop target, you can just call this method on it and wpf will do all the work. All you need to know is the index of the drop target item. This handles both vertical and horizontal scrolling.
this.listView.ScrollIntoView(this.listView.Items[index]);
When you use this method, your adorner might move with the scrolling ListBox. To fix this, I just set my adorner parent and adorner layer parent to the content of the window at the top of the visual tree (i.e. topWindow.Content
).
I'm still messing around with this exact same issue too. I'm using a slightly modified version of Bea's Drag and Drop found here, which is in VB instead of C#. When I used ScrollIntoView as described above, I could scroll down but not up. So I messed around and came up with this as my 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
and I had to include this utility function, taken from 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
which is great and all. Then running out what theuberk mentioned above with the adorner moving, and in the spirit of making this easy for someone else later, I added a variable to the DragDropAdorner class:
Private m_mouseDelta As Point
Added this to the last line of DragSource_PreviewMouseLeftButtonDown:
Me.m_mouseDelta = e.GetPosition(m_sourceItemContainer)
And turned ShowDraggedAdorner into:
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