C# 列表框DragDrop自动滚动问题?
我已使用以下方法成功地在列表框中实现拖放: Bea Stollnitz-如何在数据绑定项控件之间拖放项: 但是,当列表有滚动条且我位于列表末尾时,在我拖动项目时,列表框不会自动滚动。C# 列表框DragDrop自动滚动问题?,c#,wpf,C#,Wpf,我已使用以下方法成功地在列表框中实现拖放: Bea Stollnitz-如何在数据绑定项控件之间拖放项: 但是,当列表有滚动条且我位于列表末尾时,在我拖动项目时,列表框不会自动滚动。 我该如何融入这种行为 编辑:当我在列表的底部边界上时,我希望它滚动,以便我可以将其放在较低的项目上?我使用附加属性来实现这一点,该属性增加了在拖动鼠标时,当鼠标移向控件的顶部或底部时滚动控件的功能。以下是附加的属性类:- public class DragDropAttProps { public sta
我该如何融入这种行为
编辑:当我在列表的底部边界上时,我希望它滚动,以便我可以将其放在较低的项目上?我使用附加属性来实现这一点,该属性增加了在拖动鼠标时,当鼠标移向控件的顶部或底部时滚动控件的功能。以下是附加的属性类:-
public class DragDropAttProps
{
public static readonly DependencyProperty ScrollOnDragDropProperty =
DependencyProperty.RegisterAttached(
"ScrollOnDragDrop",
typeof(bool),
typeof(DragDropAttProps),
new PropertyMetadata(false, HandleScrollOnDragDropChanged));
public static bool GetScrollOnDragDrop(DependencyObject element)
{
return (bool)element.GetValue(ScrollOnDragDropProperty);
}
public static void SetScrollOnDragDrop(DependencyObject element, bool value)
{
element.SetValue(ScrollOnDragDropProperty, value);
}
private static void HandleScrollOnDragDropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var container = d as FrameworkElement;
if (d == null)
{
Debug.Fail("Invalid type!");
}
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)
{
const double Tolerance = 60;
const double Offset = 20;
var container = sender as FrameworkElement;
if (container == null)
{
return;
}
var scrollViewer = GetFirstVisualChild<ScrollViewer>(container);
if (scrollViewer == null)
{
return;
}
var verticalPos = e.GetPosition(container).Y;
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);
}
}
private static T GetFirstVisualChild<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
if (child is T)
{
return (T)child;
}
var childItem = GetFirstVisualChild<T>(child);
if (childItem != null)
{
return childItem;
}
}
}
return null;
}
}
公共类DragDropAttProps
{
公共静态只读从属属性ScrollOnDragDropProperty=
DependencyProperty.RegisterAttached(
“ScrollOnDragDrop”,
类型(bool),
类型(DragDropAttProps),
新的PropertyMetadata(错误,HandleScrollOnDragDropChanged));
公共静态bool GetScrollOnDragDrop(DependencyObject元素)
{
返回(bool)元素.GetValue(ScrollOnDragDropProperty);
}
公共静态void setCrollondRagdrop(DependencyObject元素,布尔值)
{
SetValue(ScrollOnDragDropProperty,value);
}
私有静态void HandleScrollOnDragDropChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
var container=d作为框架元素;
如果(d==null)
{
调试失败(“无效类型!”);
}
取消订阅(容器);
if(true.Equals(e.NewValue))
{
订阅(容器);
}
}
私有静态void订阅(FrameworkElement容器)
{
container.PreviewDragOver+=OnContainerPreviewDragOver;
}
私有静态无效取消订阅(FrameworkElement容器)
{
container.PreviewDragOver-=OnContainerPreviewDragOver;
}
ContainerPreviewDragover上的私有静态无效(对象发送器、DragEventArgs e)
{
常数双公差=60;
常数双偏移=20;
var container=发送方作为框架元素;
if(容器==null)
{
返回;
}
var scrollViewer=GetFirstVisualChild(容器);
如果(scrollViewer==null)
{
返回;
}
var verticalPos=e.GetPosition(容器).Y;
if(垂直位置<公差)
{
//可见列表的顶部?向上滚动。
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset-Offset);
}
否则如果(垂直位置>容器实际高度-公差)
{
//可见列表的底部?向下滚动。
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset+Offset);
}
}
私有静态T GetFirstVisualChild(DependencyObject depObj),其中T:DependencyObject
{
if(depObj!=null)
{
for(var i=0;i
最后,在控件的XAML上指定附加属性。免责声明:我在TreeView上使用此方法,但它应适用于任何可滚动控件,如列表框:-
<UserControl xmlns:attProps="clr-namespace:MyAssembly.AttachedProperties;assembly=MyAssembly">
<TreeView attProps:DragDropAttProps.ScrollOnDragDrop="True">
这是一个使用列表框并进行自动滚动的简单而完整的拖放示例。我认为Andrew Stephens的回答一直在回答您的问题,因为它管理autoscroll,并且使用
附加属性
,这是一个好的做法
这是一个在示例窗口中从同一列表框拖放到同一列表框的完整示例
代码隐藏:
public partial class MainWindow : Window
{
private const int dragMargin = 10;
public MainWindow()
{
InitializeComponent();
}
private void ListBox_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed || list.SelectedItem == null)
return;
if (e.MouseDevice.DirectlyOver != null)
{
DragDrop.DoDragDrop(list, list.SelectedItem, DragDropEffects.All);
}
}
private void list_Drop(object sender, DragEventArgs e)
{
var source = sender as ListBox;
if (source == null)
return;
var item = e.Data.GetData(typeof (ContentControl));
if (item == null)
return;
source.Items.Remove(item);
var listAsTarget = sender as ListBox;
if (listAsTarget == null)
return;
var mouseDirectitem = e.OriginalSource;
var listItem = VisualFindParentFromType<ListBoxItem>(mouseDirectitem as UIElement);
if (listItem == null)
{
listAsTarget.Items.Insert(0, item);
return;
}
var insertBefore = true;
var pointReletiveItem = e.GetPosition(listItem);
Debug.WriteLine("Item Height: {0}", listItem.ActualHeight);
Debug.WriteLine("Y: {0}", pointReletiveItem.Y);
if (pointReletiveItem.Y > listItem.ActualHeight / 2)
insertBefore = false;
var index = listAsTarget.Items.IndexOf(listItem.Content);
if (index >= 0)
{
listAsTarget.Items.Insert(insertBefore ? index : index + 1, item);
return;
}
listAsTarget.Items.Add(item);
}
private T VisualFindParentFromType<T>(DependencyObject element) where T : UIElement
{
if (element == null)
return default(T);
if (element is T)
return (T)element;
return VisualFindParentFromType<T>(VisualTreeHelper.GetParent(element));
}
private T FindChildrenFromType<T>(DependencyObject element) where T : UIElement
{
if (element == null)
return default(T);
if (element is T)
return (T) element;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var result = FindChildrenFromType<T>(VisualTreeHelper.GetChild(element, i));
if (!Equals(default(T), result))
return result;
}
return default(T);
}
private void list_DragOver(object sender, DragEventArgs e)
{
var source = e.Source as ListBox;
if (source == null)
return;
var point = e.GetPosition(source);
var scrollViewer = FindChildrenFromType<ScrollViewer>(source);
if (scrollViewer == null)
return;
if (point.Y < dragMargin)
{
scrollViewer.LineUp();
}
else if (point.Y > Math.Abs(source.ActualHeight - dragMargin))
{
scrollViewer.LineDown();
}
}
}
公共部分类主窗口:窗口
{
私有常量int dragMargin=10;
公共主窗口()
{
初始化组件();
}
私有无效列表框\u MouseMove(对象发送方,MouseEventArgs e)
{
如果(例如:LeftButton!=鼠标按钮状态。按下| |列表。选择编辑项==null)
返回;
if(e.MouseDevice.DirectlyOver!=null)
{
DragDrop.DoDragDrop(list,list.SelectedItem,DragDropEffects.All);
}
}
私有作废列表(对象发送方、DragEventArgs e)
{
var source=发送方作为列表框;
if(source==null)
返回;
var item=e.Data.GetData(typeof(ContentControl));
如果(项==null)
返回;
source.Items.Remove(项目);
var listAsTarget=发送方作为列表框;
如果(listAsTarget==null)
返回;
var mouseDirectitem=e.OriginalSource;
var listItem=VisualFindParentFromType(作为UIElement的mouseDirectitem);
如果(listItem==null)
{
listAsTarget.Items.Insert(0,item);
返回;
}
var insertBefore=true;
var pointrelativeItem=e.GetPosition(listItem);
WriteLine(“项高度:{0}”,listItem.ActualHeight);
WriteLine(“Y:{0}”,pointrelativeItem.Y);
如果(PointRelativeItem.Y>listItem.ActualHeight/2)
insertBefore=false;
var index=listAsTarget.Items.IndexOf(listItem.Content);
如果(索引>=0)
{
listAsTarget.Items.Insert(insertBefore?索引:索引+1,项);
返回;
}
listAsTarget.Items.Add(item);
}
私有T VisualFindParentFromType(DependencyObject元素),其中T:UIElement
{
if(元素==null)
返回默认值(T);
if(元素为T)
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox x:Name="list" Height="90" AllowDrop="True" MouseMove="ListBox_MouseMove" Drop="list_Drop" PreviewDragOver="list_DragOver">
<ContentControl>
Item 1
</ContentControl>
<ContentControl>
Item 2
</ContentControl>
<ContentControl>
Item 3
</ContentControl>
<ContentControl>
Item 4
</ContentControl>
<ContentControl>
Item 5
</ContentControl>
<ContentControl>
Item 6
</ContentControl>
<ContentControl>
Item 7
</ContentControl>
<ContentControl>
Item 8
</ContentControl>
<ContentControl>
Item 9
</ContentControl>
</ListBox>
</Grid>