ListBox fluid DragDrop with moving list
If you want having a ListBox with moving item and drag (fluid behaviror) and moving list you can use this
two class for attached properties
- DragDropExtension for moving listbox item on dragdrop
- ListBoxMoveHelper for specify drag object (in this sample )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
/// <summary> /// Provides extended support for drag drop operation /// </summary> public static class DragDropExtension { #region Offset (ATTACHED) public static double GetOffset(DependencyObject obj) => (double)obj.GetValue(OffsetProperty); public static void SetOffset(DependencyObject obj, double value) => obj.SetValue(OffsetProperty, value); public static readonly DependencyProperty OffsetProperty = DependencyProperty.RegisterAttached( "Offset", typeof(double), typeof(DragDropExtension), new FrameworkPropertyMetadata(1.0)); #endregion #region Tolerance (ATTACHED) public static double GetTolerance(DependencyObject obj) => (double)obj.GetValue(ToleranceProperty); public static void SetTolerance(DependencyObject obj, double value) => obj.SetValue(ToleranceProperty, value); public static readonly DependencyProperty ToleranceProperty = DependencyProperty.RegisterAttached( "Tolerance", typeof(double), typeof(DragDropExtension), new FrameworkPropertyMetadata(60.0)); #endregion #region ScrollOnDragDropProperty (ATTACHED) public static bool GetScrollOnDragDrop(DependencyObject element) { return (bool)element.GetValue(ScrollOnDragDropProperty); } public static void SetScrollOnDragDrop(DependencyObject element, bool value) { element.SetValue(ScrollOnDragDropProperty, value); } public static readonly DependencyProperty ScrollOnDragDropProperty = DependencyProperty.RegisterAttached("ScrollOnDragDrop", typeof(bool), typeof(DragDropExtension), new PropertyMetadata(false, (d, e) => { FrameworkElement container = d as FrameworkElement; if (d == null) { Debug.Fail("Invalid type!"); return; } Unsubscribe(container); if (true.Equals(e.NewValue)) { Subscribe(container); } } )); private static void Subscribe(FrameworkElement container) { container.PreviewDragOver += OnContainerPreviewDragOver; } private static void Unsubscribe(FrameworkElement container) { container.PreviewDragOver -= OnContainerPreviewDragOver; } private static void OnContainerPreviewDragOver(object sender, DragEventArgs e) { FrameworkElement container = sender as FrameworkElement; if (container == null) { return; } ScrollViewer scrollViewer = GetFirstVisualChild<ScrollViewer>(container); if (scrollViewer == null) { return; } double tolerance = GetTolerance(container); double verticalPos = e.GetPosition(container).Y; double offset = GetOffset(container); if (verticalPos < tolerance) // Top of visible list? { scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - offset); //Scroll up. } else if (verticalPos > container.ActualHeight - tolerance) //Bottom of visible list? { scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + offset); //Scroll down. } } #endregion #region STATIC public static T GetFirstVisualChild<T>(DependencyObject depObj) where T : DependencyObject { if (depObj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child != null && child is T) { return (T)child; } T childItem = GetFirstVisualChild<T>(child); if (childItem != null) { return childItem; } } } return null; } #endregion } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
public class ListBoxMoveHelper { /// <summary> /// current index (in listbox) for dragger /// </summary> #region CurrentDragIndex (ATTACHED) public static int GetCurrentDragIndex(DependencyObject obj) => (int)obj.GetValue(CurrentDragIndexProperty); public static void SetCurrentDragIndex(DependencyObject obj, int value) => obj.SetValue(CurrentDragIndexProperty, value); // Using a DependencyProperty as the backing store for CurrentDragIndex. This enables animation, styling, binding, etc... public static readonly DependencyProperty CurrentDragIndexProperty = DependencyProperty.RegisterAttached( "CurrentDragIndex", typeof(int), typeof(ListBoxMoveHelper), new FrameworkPropertyMetadata(-1)); #endregion /// <summary> /// Define object used for draging element /// </summary> #region Dragger (ATTACHED) public static bool GetDragger(DependencyObject obj) => (bool)obj.GetValue(DraggerProperty); public static void SetDragger(DependencyObject obj, bool value) => obj.SetValue(DraggerProperty, value); // Using a DependencyProperty as the backing store for Dragger. This enables animation, styling, binding, etc... public static readonly DependencyProperty DraggerProperty = DependencyProperty.RegisterAttached( "Dragger", typeof(bool), typeof(ListBoxMoveHelper), new FrameworkPropertyMetadata(false, (ss, ee) => { FrameworkElement control = ss as FrameworkElement; DragEventHandler deh = (s, e) => { //try //{ ListBox listbox = s as ListBox; Point pt = e.GetPosition(listbox); int currentIndex = GetCurrentDragIndex(listbox); var x = VisualTreeHelper.HitTest(listbox, pt); var dt = (x.VisualHit as FrameworkElement).DataContext; var newIndex = (listbox.ItemsSource as IList).IndexOf(dt); Console.WriteLine($"DATACONTEXT = {dt} new index = {newIndex}"); (listbox.ItemsSource as IList).Show(); if (newIndex != -1 && currentIndex != newIndex) { Type t = listbox.ItemsSource.GetType(); MethodInfo move = t.GetMethod("Move"); move.Invoke(listbox.ItemsSource, new object[] { currentIndex, newIndex }); currentIndex = newIndex; SetCurrentDragIndex(listbox, currentIndex); Console.WriteLine($"MOUSE MOVE index = {GetCurrentDragIndex(control)}"); } //} //catch { } }; control.PreviewMouseDown += (s, e) => { ListBox listbox = FindParent<ListBox>(control); listbox.AllowDrop = true; int currentIndex = (listbox.ItemsSource as IList) .IndexOf(control.DataContext); SetCurrentDragIndex(listbox, currentIndex); listbox.DragOver += deh; Console.WriteLine($"MOUSE DOWN index = {currentIndex}"); (listbox.ItemsSource as IList).Show(); DragDrop.DoDragDrop(listbox, "Dragger", DragDropEffects.Move); }; control.PreviewMouseUp += (s, e) => { ListBox listbox = FindParent<ListBox>(control); listbox.AllowDrop = false; listbox.DragOver -= deh; SetCurrentDragIndex(listbox, -1); }; }) ); #endregion #region Static functions private static T FindParent<T>(DependencyObject child) where T : DependencyObject { DependencyObject parentObject = VisualTreeHelper.GetParent(child); if (parentObject == null) return null; T parent = parentObject as T; return parent ?? FindParent<T>(parentObject); } #endregion } |
use like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<ListBox BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Margin="10,15,0,77" ItemsSource="{Binding List}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" local:DragDropExtension.ScrollOnDragDrop="true" local:DragDropExtension.Tolerance="25" FontSize="20" Width="355"> <ListBox.ItemTemplate> <DataTemplate> <DockPanel> <TextBlock x:Name="move" local:ListBoxMoveHelper.Dragger="true" Text="E" DockPanel.Dock="Right" Cursor="Hand" FontFamily="/#Vermin Vibes 2"/> <TextBlock Text="{Binding .}" /> </DockPanel> </DataTemplate> </ListBox.ItemTemplate> <!-- ITEM PANEL with fluid --> <ListBox.ItemsPanel> <ItemsPanelTemplate> <StackPanel HorizontalAlignment="Stretch"> <i:Interaction.Behaviors> <ei:FluidMoveBehavior Duration="0:0:00.5" AppliesTo="Children"> <ei:FluidMoveBehavior.EaseY> <CircleEase EasingMode="EaseOut" /> </ei:FluidMoveBehavior.EaseY> <ei:FluidMoveBehavior.EaseX> <BackEase EasingMode="EaseOut" /> </ei:FluidMoveBehavior.EaseX> </ei:FluidMoveBehavior> </i:Interaction.Behaviors> </StackPanel> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> |
for result
for information in DragDropExtension
- Tolerance is the margin (top or bottom) for start moving item in ListBox
- Offset is the number of item to move
Categories: Non classé
Commentaires récents