使用附加属性的 ListBox 拖放






4.92/5 (9投票s)
使用附加属性在 Listbox 之间启用拖放(仅一行代码)。
注释
有关附加属性的更详细说明,请参见此处。
引言
在完成 GlassEffect 文章之后,我开始梦想更多使用附加属性的方法。还有什么比使用它来启用 Listbox
控件上的拖放功能更好的方法呢?
DragAndDrop
类将由我提供两个附加属性:DragEnabled
用于启用 ListBox
上的拖动,DropEnabled
用于启用 ListBox
上的放置。这可以应用于相同的 ListBox
或多个 ListBox
控件。要启用拖动,只需添加以下一行代码
<ListBox x:Name="SourceListBox" src:DragAndDrop.DragEnabled="true"/>
并添加以下内容以允许放置
<ListBox x:Name="DestinationListBox" src:DragAndDrop.DropEnabled="true"/>
拖放
在 WPF 中实现拖放相对容易。以下是两个附加属性的定义
#region DragEnabled
public static readonly DependencyProperty DragEnabledProperty =
DependencyProperty.RegisterAttached("DragEnabled", typeof(Boolean),
typeof(DragAndDrop), new FrameworkPropertyMetadata(OnDragEnabledChanged));
public static void SetDragEnabled(DependencyObject element, Boolean value)
{
element.SetValue(DragEnabledProperty, value);
}
public static Boolean GetDragEnabled(DependencyObject element)
{
return (Boolean)element.GetValue(DragEnabledProperty);
}
public static void OnDragEnabledChanged
(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if ((bool)args.NewValue == true)
{
ListBox listbox = (ListBox)obj;
listbox.PreviewMouseLeftButtonDown +=
new MouseButtonEventHandler(listbox_PreviewMouseLeftButtonDown);
}
}
#endregion
#region DropEnabled
public static readonly DependencyProperty DropEnabledProperty =
DependencyProperty.RegisterAttached("DropEnabled", typeof(Boolean),
typeof(DragAndDrop), new FrameworkPropertyMetadata(OnDropEnabledChanged));
public static void SetDropEnabled(DependencyObject element, Boolean value)
{
element.SetValue(DropEnabledProperty, value);
}
public static Boolean GetDropEnabled(DependencyObject element)
{
return (Boolean)element.GetValue(DropEnabledProperty);
}
public static void OnDropEnabledChanged
(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if ((bool)args.NewValue == true)
{
ListBox listbox = (ListBox)obj;
listbox.AllowDrop = true;
listbox.Drop += new DragEventHandler(listbox_Drop);
}
}
#endregion
重要的是要注意,要启用拖动,我们向 PreviewMouseLeftButtonDown
添加一个处理程序;要启用项目的放置,我们将 AllowDrop
设置为 true
并为 Drop
事件添加一个处理程序。
在我们实现事件处理程序之前,让我们也看看这个类的其他两个重要部分。首先,让我们看看我们用来确定基于鼠标点击位置正在拖动的项目的辅助类。我们在这里使用提供的命中测试。此代码取自此处
#region Helper
private static object GetObjectDataFromPoint(ListBox source, Point point)
{
UIElement element = source.InputHitTest(point) as UIElement;
if (element != null)
{
object data = DependencyProperty.UnsetValue;
while (data == DependencyProperty.UnsetValue)
{
data = source.ItemContainerGenerator.ItemFromContainer(element);
if (data == DependencyProperty.UnsetValue)
element = VisualTreeHelper.GetParent(element) as UIElement;
if (element == source)
return null;
}
if (data != DependencyProperty.UnsetValue)
return data;
}
return null;
}
#endregion
我们还有两个需要了解的 static
对象。一旦鼠标单击某个项目,我就会保留对其 ListBox
的引用,并且还会记住其 ListBoxItem
内容的类型。我为什么要这样做?ListBoxItem
可以包含两种类型的项目:ListBox
可以包含 UIElement
(例如 TextBlock
、Image
等),也可以绑定到数据结构(CLR 对象、XML 数据等)。这些需要以不同的方式处理,因此我们需要知道它的类型!稍后我将解释如何处理每种类型...
接下来,让我们看看启用拖动的处理
static void listbox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragSource = (ListBox)sender;
object data = (object)GetObjectDataFromPoint(DragSource, e.GetPosition(DragSource));
if (data != null)
{
DragType = data.GetType();
DragDrop.DoDragDrop(DragSource, data, DragDropEffects.Copy);
}
我们将 DragSource
设置为发送 ListBox
。接下来,使用 GetObjectDataFromPoint
辅助函数来确定我们是否正在拖动有效项目。还要存储拖动项目的类型。最后,我们调用 DragDrop.DoDragDrop
。这会处理其余的工作!
static void listbox_Drop(object sender, DragEventArgs e)
{
object data = e.Data.GetData(DragType);
if (DragType.IsVisible == true)
DragSource.Items.Remove(data);
((ListBox)sender).Items.Add(data);
}
这是真正发生动作的地方。当放置一个项目时,会调用此事件。它返回被放置的项目。在这里我们需要知道被放置的项目的类型。如果它是 UIElement
,那么我们将收到一个等于 typeof(ListBoxItem)
的类型。我们需要从源中删除它,否则我们会收到一个异常!现在剩下的就是将放置的数据添加到我们的目标 ListBox
中!
附加属性非常棒!它为我们提供了一种简洁明了的方式来启用现有控件的功能,并且像往常一样......只需一行代码即可打开它!!!
历史
- 2008 年 1 月 11 日:首次发布