Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Drag&Drop Auto-scroll

0.00/5 (No votes)
12 Aug 2013 1  
Automatic scrolling while drag&drop operation in WPF. For this article the functionality is realized as an Attached Property.

Introduction 

Based on an answer from akjoshi on Stackoverflow (http://stackoverflow.com/questions/1316251/wpf-listbox-auto-scroll-while-dragging ) this Attached-Behavior brings you automatic scrolling when dragging over a control in WPF. This is an adapted version; the short example on Stackoverflow does not respect all facets of different controls and their scroll behavior. Support for acceleration towards the edges was also added. So here is an improved version. There is no version for System.Interactivity. However it should be easy to port it and make it a Behavior.

What is it?

This article provides you with an attached property, that can be used within XAML code. It will add auto-scroll capability to every control that has a ScrollViewer. In fact, this code does a deep search in the visual tree and grabs the first ScrollViewer it can get ahold of. Practically making any visual tree with a ScrollViewer to scroll automatically once a drag operation is in progress. That means: Since the Control-Templates of a ListView, TreeView or a ListBox usually have got a ScrollViewer contained, applying that attribute to the Control will enable the auto-scroll functionality. 

Look and behavior

The look is not existing. However if the user is in a drag operation and he/she drags over such a prepared control and approaches the upper or lower edges of the scrollable control area, it magically starts scrolling. The auto scroll starts at abut 40 WPF-pixel units or 25% of the height. Whatever is smaller (respecting very small controls). These limits are hard coded and respect different DIP-settings/scale factors. The speed of the auto scrolling varies in position. The closer the user drags towards the upper or lower edge, the faster it scrolls.

Using the code

Just use the code like any other attribute, too: Add the namespace to your XAML file:

<Window x:Class="Wpf_Drag_Drop_Scroll.MainWindow" 
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:dds="clr-namespace:Wpf_Drag_Drop_Scroll"
   Title="Auto-Scrollon Drag - Demo" >
   ....   

Then set the ScrollOnDragDrop property to true on the scrollable control in question:

<ListBox Name="listBox1" 
    dds :DragDropExtension.ScrollOnDragDrop="True"
    AllowDrop="True">
    <Button>Blalba</Button>
    <Button>Blalba</Button>
    <Button>Blawesdgs</Button>
    ...
 </ListBox> 

Make sure you also have the AllowDrop property set to true. Anyway, see the demo project that provides and demonstrates the code.

Restrictions

It only works vertically. However it should not be hard to create a second attached property, which would do the same for horizontal. I’d suggest two properties for the flexibility of use. Also there is no hook to circumvent scrolling depending on the dragged object. So regardless of what is under your mouse cursor, the scrolling works.  

Lessons learned

The lesson one can learn off that implementation is this: The ScrollViewer behaves differently depending on content and conditions. Namely the ScrollableHeight and the VerticalOffset properties have got quite different values. You can observe this, if you use a vanilla ListBox and for example a generic ScrollViewer around a Canvas. The ScrollViewer of the ListBox will treat every line as 1 unit (thus ScrollableHeight == #Elements), while the ScrollViewer around the Canvas treats every pixel as a unit. This is because of the ScrollViewer.CanContentScrollProperty property that each control supplies (tree-inherits) to the ScrollViewer. This property is affected by the virtualization-capability of certain WPF-controls. See ItemsContainer=StackPanel vs. VirtualizingStackPanel. If content is virtualized, it's size has not been determined. Yet, it can't be. Thus the ScrollableHeight is indefinite and as a resolution all Items are considered to be the same height: 1 unit. If content however is not virtualized, it is well available for measure etc. Thus the ScrollViewer can use Pixel units (even fractions) to work with it. This different behavior has been taken into account when this code was written. This bit is one of the extensions compared with the linked base code. As a result, the code scrolls virtualized content 1..3 lines as of different speeds, while scrollable content is speed up between 1 and 30 pixel depending on position of the mouse cursor. 

Example Program 

Attached is a test program to check out the behavior of the code. Use the aquamarine colored ellipse to start dragging. Provided are different controls to test the code with their differently behaving ScrollViewers. On the left, we have got a ListBox with virtualized content. Therefore scroll speeds vary digitally from 1..3. The second control is a Canvas (arbirtrary image) inside a ScrollViewer. Here and in the next two controls content is scrollable (ScrollViewer.CanContentScrollProperty=true) and the movement is pixel-wise. Speed varies 1..30. The last two controls are: A WrapPanel inside a ScrollViewer and a TreeView with it's intrinsic ScrollViewer

Last words

Use this code in many places in your application and make your users happier. And thanks to akjoshi for the nice base work!

History 

First version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here