Прокрутка при перетаскивании (WPF)

Хорошо, ребята, я почесал голову, как сумасшедший по этой проблеме, и потратил несколько часов, пытаясь исследовать, как это работает, но я еще не нашел ответа, если вы хотите, чтобы какой-либо из моих SRC чувствовал себя свободно спросить об этом, и я посмотрю, смогу ли я помочь.

В основном проблема, с которой я сталкиваюсь, заключается в том, что у меня есть TreeView папок в моем приложении i.e:

Catalog

  Brands
    Nike
    Adidas
    Lactose

  Styles
    Sandles
    Trainers
    Boots

Проблема, которую я пытаюсь исправить, заключается в том, что когда я перетаскиваю папку (это обрабатывается в классе DragDropManager), я не могу прокручивать вверх или вниз (просто отображает прекрасный знак остановки). Я также не могу найти скроллер на самом деле в дереве, поэтому я не уверен, как он создается (это не мое собственное программное обеспечение, я недавно начал работать в компании, поэтому я не знаком с кодом, и никто другой похоже, знает.)

Это проблема, если я хочу переместить что-то с самого верхнего на самое нижнее.

Скроллинг работает отлично без перетаскивания.

Если кто-то хочет увидеть какую-либо часть моего кода, не стесняйтесь спрашивать, поскольку я не уверен, что на самом деле показать вам, ребята.

Я прочитал несколько хороших статей и просто оставил царапины на голове.

Ответ 1

Я создал прикрепленное свойство для достижения такого поведения, посмотрите мой пост здесь -

Приложенное поведение для автоматической прокрутки контейнеров при выполнении Drag and Drop

Основная логика - это что-то вроде этого -

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);     
    }
}

Аналогичные вопросы по SO (хотя они в основном для ListBox/ListView, но должны работать и для TreeView) -

Автоматическая прокрутка списка WPF при перетаскивании

WPF ListView Databound Перетаскивание автоматической прокрутки

WPF Drag-to-scroll работает неправильно

Ответ 2

Я знаю этот вопрос очень старый, но вот MVVM-путь как прикрепленное свойство:

    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
        }
    }

И это может быть использовано как:

<Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
    <Setter Property="tool:TreeItemAttach.BringIntoView" Value="True"/>
</Style>