我使用以下方法在ListBox中成功实现了拖放:
Bea Stollnitz - 如何在数据绑定ItemsControls之间拖放项目: http://www.zagstudio.com/blog/488#.U15ozvldU9Y
但是,当列表中有一个滚动条并且我在列表的末尾时,ListBox不会在我拖动项目时自动滚动。
我该如何融入这种行为?
答案 0 :(得分:2)
我使用附加属性来实现这一点,该属性添加了在拖动时鼠标朝向顶部或底部移动时滚动控件的功能。这是附加的属性类: -
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;
}
}
最后,在控件的XAML上指定附加属性。免责声明:我在TreeView上使用这种方法,但它 应该在任何可滚动控件上工作,例如列表框: -
<UserControl xmlns:attProps="clr-namespace:MyAssembly.AttachedProperties;assembly=MyAssembly">
<TreeView attProps:DragDropAttProps.ScrollOnDragDrop="True">
答案 1 :(得分:0)
这是一个使用ListBox并进行自动滚动的简单而完整的拖放示例。我认为安德鲁斯蒂芬斯的回答是在回答你的问题,因为它管理自动滚动,并使用attached properties
,这是一个很好的做法。
这是完整拖拽的示例。放入一个示例窗口,从同一个列表框中删除。
代码背后:
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();
}
}
}
的Xaml:
<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>
希望这会有所帮助......