拖放时滚动(WPF)

Posted

技术标签:

【中文标题】拖放时滚动(WPF)【英文标题】:Scrolling while dragging and dropping (WPF) 【发布时间】:2012-05-30 19:21:11 【问题描述】:

好的,伙计们,我一直在为这个问题抓狂,并且花了好几个小时试图研究它是如何工作的,但我还没有找到答案,如果你希望看到我的任何 SRC 随意问一下,我会看看我是否可以提供帮助。

基本上我遇到的问题是我的应用程序中有一个TreeView 文件夹,即:

Catalog

  Brands
    Nike
    Adidas
    Lactose

  Styles
    Sandles
    Trainers
    Boots

我要解决的问题是,当我拖动文件夹时(这在我的DragDropManager 类中处理),我无法向上或向下滚动(仅显示一个可爱的停止标志)。我实际上也无法在树视图中找到滚动条,所以我不确定它是如何生成的(这不是我自己的软件,我最近开始为一家公司工作,所以我不熟悉代码,也没有其他人好像知道了。)

如果我想将某些东西从最顶端移到最底端,这将是一个问题。

滚动无需拖动即可自行正常工作。

如果有人希望看到我的代码的任何部分,请随时询问,因为我不确定要向你们展示什么。

我已经阅读了好几篇文章,只是摸不着头脑。

【问题讨论】:

【参考方案1】:

我已经创建了一个附加属性来实现这个行为,看看我的帖子在这里 -

Attached Behavior for auto scrolling containers while doing Drag & 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 Listbox auto scroll while dragging

WPF ListView Databound Drag/Drop Auto Scroll

WPF Drag-to-scroll doesn't work correctly

【讨论】:

我一直在尝试在我的列表框上使用它,但如果列表框中的项目无法拖放,它就不起作用。我在列表框中混合了项目,有些可以放下,有些不能。如果靠近顶部(或底部)的那些无法接收掉落,则不会激活滚动。有什么想法可以解决这个问题吗? @Lutz 这很有趣,从来没有遇到过这样的场景,所以不能多说。我可以想到 1. 在当前项目之间添加一些虚拟项目(高度小等,因此它们看起来不会奇怪或浪费空间),使它们可放置,以便始终调用 PreviewDragOver。但是您将不得不处理丢弃等。 2. 使所有项目可丢弃并在丢弃后处理验证。 3. 尝试查找是否为这些不可放置的项目触发了其他事件。 @nikotromus 你能详细说明什么不起作用吗?还有你的场景等等! @akjoshi - 我已经完全按照您的指定添加了您的代码。但是,当我将行 'WpfExtensions:DragDropExtension.ScrollOnDragDrop="True"' 添加到我的 ListView 时,它给了我以下智能错误:“名称 DragDropExtension 不存在于命名空间 'clr-namespace:WpfExtensions' ”跨度> 这个扩展也适用于 ScrollViewers。但是您必须将 ScrollViewer 包装在 Border 中,并将扩展名添加到边框而不是 ScrollViewer。【参考方案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>

【讨论】:

以上是关于拖放时滚动(WPF)的主要内容,如果未能解决你的问题,请参考以下文章

使用html5拖放时滚动[重复]

C#在拖放时在ListView中实现自动滚动

C#在拖放时实现ListView中的自动滚动

拖放时吞下异常

jquery html在拖放时复制

在ANDROID中相互拖放时合并Recyclerview中的项目?