Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scrolling while dragging and dropping (WPF)

Okay guys, I have been scratching my head like mad over this issue and have spent a good few hours trying to research how it works but I am yet to find an answer, if you wish to see any of my SRC feel free to ask about it and I will see if I can help.

Basically the issue I am having is that I have a TreeView of folders in my application i.e.:

Catalog

  Brands
    Nike
    Adidas
    Lactose

  Styles
    Sandles
    Trainers
    Boots

The issue that I am trying to fix is that when I drag a folder around (This is handled in my DragDropManager class), I am unable to scroll up or down(simply displays a lovely stop sign). I am also unable to find a scroller actually within the TreeView, so I am unsure how it is being generated (This is not my own software, I have recently started working for a company so I am not familiar with the code and no one else seems to know.)

This is a problem if I want to move something from the very top to the very bottom.

The scrolling works fine on its own without the dragging being done.

If anyone wishes to see any part of my code feel free to ask as I am unsure what to actually show you guys.

I have read through a good few articles and am just left scratching my head.

like image 806
Hello World Avatar asked May 24 '12 08:05

Hello World


2 Answers

I have created an attached property for achieving this behavior, have a look at my post here -

Attached Behavior for auto scrolling containers while doing Drag & Drop

Main logic is something like this -

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 = 60;
    double verticalPos = e.GetPosition(container).Y;
    double offset = 20;

    if (verticalPos < tolerance) // Top of visible list? 
    {
        //Scroll up
        scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - offset);
    }
    else if (verticalPos > container.ActualHeight - tolerance) //Bottom of visible list? 
    {
        //Scroll down
        scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + offset);     
    }
}

Similar questions on SO (although they are mostly for ListBox/ListView but should work for TreeView too) -

WPF Listbox auto scroll while dragging

WPF ListView Databound Drag/Drop Auto Scroll

WPF Drag-to-scroll doesn't work correctly

like image 158
akjoshi Avatar answered Oct 17 '22 15:10

akjoshi


I know this question really old, but here is the MVVM way as attached property:

    using System.Windows;
    using System.Windows.Controls;

    namespace AndroidCtrlUI.XTools.Behaviors
    {
        ///<summary>
        /// TreeItemAttach 
        ///<para/> TreeViewItem
        ///</summary>
        public sealed class TreeItemAttach
        {
            #region BringIntoView

            ///<summary>
            /// DependencyProperty
            ///</summary>
            public static readonly DependencyProperty BringIntoViewProperty = DependencyProperty.RegisterAttached("BringIntoView", typeof(bool), typeof(TreeItemAttach), new UIPropertyMetadata(false, (s, e) =>
            {
                if ((bool)e.NewValue != (bool)e.OldValue && s is TreeViewItem t)
                {
                    if ((bool)e.NewValue)
                    {
                        t.Selected += BringIntoView;
                    }
                    else
                    {
                        t.Selected -= BringIntoView;
                    }
                }
            }));

            ///<summary>
            /// Get
            ///</summary>
            ///<param name="target">DependencyObject</param>
            ///<returns>ICommand</returns>
            public static bool GetBringIntoView(DependencyObject target)
            {
                return (bool)target.GetValue(BringIntoViewProperty);
            }

            ///<summary>
            /// Set
            ///</summary>
            ///<param name="target">DependencyObject</param>
            ///<param name="value">ICommand</param>
            public static void SetBringIntoView(DependencyObject target, bool value)
            {
                target.SetValue(BringIntoViewProperty, value);
            }

            private static void BringIntoView(object sender, RoutedEventArgs e)
            {
                if (e.Source is TreeViewItem s)
                {
                    double h = s.ActualHeight;
                    if (s.IsExpanded && s.Items.Count > 0)
                    {
                        h = s.ActualHeight / TreeWalker(s);
                    }
                    s.BringIntoView(new Rect(0, h * -1, s.ActualWidth, h * 2.5));
                }
            }

            private static long TreeWalker(TreeViewItem item)
            {
                long c = item.Items.Count;
                foreach (object i in item.Items)
                {
                    if (i != null && item.ItemContainerGenerator.ContainerFromItem(i) is TreeViewItem t && t.IsExpanded && t.Items.Count > 0)
                    {
                        c += TreeWalker(t);
                    }
                }
                return c;
            }
            #endregion
        }
    }

And it can be used like:

<Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
    <Setter Property="tool:TreeItemAttach.BringIntoView" Value="True"/>
</Style>
like image 31
k1ll3r8e Avatar answered Oct 17 '22 15:10

k1ll3r8e