WPF: Scroll content of control when drag & drop is in progress
It's nice to be able to scroll through content of a control when items are being dragged over a WPF control.
I have created an attached property called "IsScrollOnDragOverEnabled", which you can set on a control with ScrollViewer as shown below to scroll the content of the control when items are being dragged over the control.
Features:
1) Horizontal Scroll
2) Vertical Scroll
3) Smooth Scroll (Note: This feature scrolls content of the control in a smooth scroll fashion, to let the users see and interact with content being scrolled.
Here is how to use it:
<Window x:Class="DragAndDropFramework.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DragAndDropBehaviors="clr-namespace:DragAndDropBehaviors;assembly=DragAndDrop" Title="Drag and Drop Demo" Height="500" Width="500"> <Grid> <ListBox AllowDrop="True" DragAndDropBehaviors:ScrollOnDragOverBehavior.IsScrollOnDragOverEnabled="True" /> </Grid> </Window>
Here is the attached property:
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace DragAndDropBehaviors { public static class ScrollOnDragOverBehavior { public static bool GetIsScrollOnDragOverEnabled(DependencyObject element) { return (bool) element.GetValue(IsScrollOnDragOverEnabledProperty); } public static void SetIsScrollOnDragOverEnabled(DependencyObject element, bool value) { element.SetValue(IsScrollOnDragOverEnabledProperty, value); } public static readonly DependencyProperty IsScrollOnDragOverEnabledProperty = DependencyProperty.RegisterAttached("IsScrollOnDragOverEnabled", typeof (bool), typeof (ScrollOnDragOverBehavior), new FrameworkPropertyMetadata(false, OnIsScrollOnDragOverPropertyChanged)); private static void OnIsScrollOnDragOverPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var dropTarget = d as FrameworkElement; if (dropTarget == null) return; if ((bool) e.OldValue) { dropTarget.PreviewDragOver -= OnPreviewDragOver; } if ((bool) e.NewValue) { dropTarget.PreviewDragOver += OnPreviewDragOver; } } private static DateTime _lastPreviewDragOverEvent = DateTime.MinValue; private static void OnPreviewDragOver(object sender, DragEventArgs e) { if (DateTime.Now.Subtract(_lastPreviewDragOverEvent).Milliseconds >= 250) { _lastPreviewDragOverEvent = DateTime.Now; var dropTarget = sender as FrameworkElement; if (dropTarget == null) { return; } var scrollViewer = FindChild<ScrollViewer>(dropTarget); if (scrollViewer == null) { return; } const double tolerance = 20; const double yOffset = 5; const double xOffset = 5; var yPos = e.GetPosition(dropTarget).Y; var xPos = e.GetPosition(dropTarget).X; if (yPos < tolerance) { SmoothVerticalScroll(scrollViewer, -yOffset); } else if (yPos > dropTarget.ActualHeight - tolerance) { SmoothVerticalScroll(scrollViewer, yOffset); } if (xPos < tolerance) { SmoothHorizontalScroll(scrollViewer, -xOffset); } else if (xPos > dropTarget.ActualWidth - tolerance) { SmoothHorizontalScroll(scrollViewer, xOffset); } } } private static void SmoothVerticalScroll(ScrollViewer scrollviewer, double offset) { if (offset > 0) { for (var i = 0; i < offset; i++) { scrollviewer.ScrollToVerticalOffset(scrollviewer.VerticalOffset + 1); } } else { for (var i = 0; i < Math.Abs(offset); i++) { scrollviewer.ScrollToVerticalOffset(scrollviewer.VerticalOffset - 1); } } } private static void SmoothHorizontalScroll(ScrollViewer scrollviewer, double offset) { if (offset > 0) { for (var i = 0; i < offset; i++) { scrollviewer.ScrollToHorizontalOffset(scrollviewer.HorizontalOffset + 1); } } else { for (var i = 0; i < Math.Abs(offset); i++) { scrollviewer.ScrollToHorizontalOffset(scrollviewer.HorizontalOffset - 1); } } } public static T FindChild(DependencyObject depObj) where T : DependencyObject { for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { var child = VisualTreeHelper.GetChild(depObj, 0); if (child != null && child is T) { return (T)child; } var childItem = FindChild (child); if (childItem != null) { return childItem; } } return null; } } }
Comments
Post a Comment