WPF 拖放 - 第二部分
一篇文章,展示如何使用 GongSolutions.Wpf.DragDrop 库向 WPF 应用程序添加拖放功能。
引言
这是关于使用 GongSolutions.Wpf.DragDrop 库向 WPF 应用程序添加拖放功能的第二篇文章。您可以在这里找到第一部分:WPF 中的拖放,第一部分。
背景
在第一篇文章中,我介绍了如何使用附加属性向 ItemsControl
添加简单的拖放行为,并展示了如何通过添加放置处理程序来定制放置行为。如果您还没有看过这篇文章,我建议您先阅读它。
Using the Code
在本文中,我将解释如何使用拖动手柄定制拖动行为,如何显示拖动装饰器,以及如何处理多选。
拖动手柄
与放置处理程序允许我们编写代码来自定义在控件上放置的处理方式一样,拖动手柄允许我们自定义拖动本身。为了演示这一点,我将使用我们在第 1 部分中构建的示例:学校示例。
在我们的应用程序中,假设有些学生不想被移出他们的学校。为了举例说明,我将通过他们的名字硬编码一个学生 - "Tom Jefferson"。在现实世界的应用程序中,您显然不会对这样的信息进行硬编码,但是为了保持示例的简单性,我将完全按照那样做!
为了防止移动 Tom Jefferson,我们首先需要将一个 DragHandler
添加到学生的 ListBox
中。
<ListBox Grid.Column="1"
ItemsSource="{Binding Schools.CurrentItem.Pupils}"
DisplayMemberPath="FullName"
dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DragHandler="{Binding}"/>
在这里,就像 DropHandler 一样,我们正在将拖动手柄绑定到 MainViewModel
。为了使 MainViewModel
成为拖动手柄,我们需要实现 IDragSource
接口
void IDragSource.StartDrag(DragInfo dragInfo)
{
PupilViewModel pupil = (PupilViewModel)dragInfo.SourceItem;
if (pupil.FullName != "Tom Jefferson")
{
dragInfo.Effects = DragDropEffects.Copy | DragDropEffects.Move;
dragInfo.Data = pupil;
}
}
IDragSource
接口仅定义一个方法,StartDrag
。在我们的实现中,我们首先检查学生的名字是否是 "Tom Jefferson",如果不是,我们告诉框架允许 Copy
和 Move
操作。接下来,我们设置拖动数据 - 必须在拖动手柄中设置 Effects
和 Data
属性才能开始拖动。
运行您的程序,就在那里:Tom Jefferson 不再可拖动。
显示拖动装饰器
拖动装饰器是一个透明图像,显示了被拖动数据的预览。当我们拖动一个学生时,我们将显示一个看起来像这样的拖动装饰器
对于我在图标上缺乏艺术技巧,请原谅,但你应该明白这个想法。随意使用您自己的更好的图标 :)
要显示装饰器,首先,我们创建一个 DataTemplate
。我们将把它放在 Window
的 Resources
部分中
<Window.Resources>
<DataTemplate x:Key="PupilDragAdorner">
<StackPanel>
<Image Source="/Person.png" Width="64"
HorizontalAlignment="Center"/>
<TextBlock Text="{Binding FullName}" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
这个 DataTemplate
由一个 StackPanel
组成,它垂直排列两个控件:一个 Image
包含图标和一个 TextBlock
来显示学生的姓名。
现在,我们只需要在我们的 ListBox
上设置 DragAdornerTemplate
附加属性
<ListBox Grid.Column="1"
ItemsSource="{Binding Schools.CurrentItem.Pupils}"
DisplayMemberPath="FullName"
dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DragHandler="{Binding}"
dd:DragDrop.DragAdornerTemplate="{StaticResource PupilDragAdorner}"/>
然后,瞧!当您从学生的 ListBox
拖动一个项目时,装饰器就会出现。
多选
如果源 ItemsControl
上可以使用多选,那么默认的拖动手柄将处理此问题。但是,当放置多个数据项时,您将不再接收单个对象,而是一个对象的**集合**。在您的放置处理程序中处理此问题的最简单方法是检查拖动数据是接受类型的对象还是接受类型的 IEnumerable
。因此,示例方法中的 DragOver
方法需要更改为这样
void IDropTarget.DragOver(DropInfo dropInfo)
{
if ((dropInfo.Data is PupilViewModel || dropInfo.Data
is IEnumerable<PupilViewModel>) &&
dropInfo.TargetItem is SchoolViewModel)
{
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
}
在这里,我们正在检查数据是 PupilViewModel
**或** IEnumerable<PupilViewModel>
。
您还需要在您的 IDropTarget.Drop
方法中处理多选的可能性,但我将其留给读者作为练习。
同样,要使 IDragSource
能够处理多选,您需要在 StartDrag
方法中查看 DragInfo.SourceItems
属性,而不是 DragInfo.SourceItem
属性。
关注点
拖动多选目前有点问题。在 WPF 中,如果用户在 ItemsControl
上进行了多选,然后单击其中一个项目以开始拖动,则多选将被清除!因此,框架会检查是否是这种情况,如果是,则吞噬点击,这样就不会丢失多选。但是,这会产生一个副作用,即如果控件中的所有项目都被选中,则它们无处可单击来取消多选! 任何关于如何解决此问题的提示都将不胜感激。