拖放概述
本教程概述了NoesisGUI中的拖放支持。拖放通常是指一种数据传输方法,该方法包括使用鼠标(或某些其他定点设备)选择一个或多个对象,将这些对象拖到用户界面中某些所需的放置目标上,然后将其放置。
image.png拖放支持
拖放操作通常涉及两个方面:拖动源是拖动对象的来源,而拖放目标则接收放置的对象。拖动源和放置目标必须是同一应用程序中的UI元素。
可以通过拖放操作的对象的类型和数量是完全任意的。例如,文件,文件夹和内容选择是通过拖放操作处理的一些较常见的对象。
在拖放操作期间执行的特定操作是特定于应用程序的,并且通常由上下文确定。例如,默认情况下,将选择的文件从一个文件夹拖到同一存储设备上的另一个文件夹将移动文件,而默认情况下,将文件从另一台设备拖到本地文件夹将复制文件。
在NoesisGUI中,任何UIElement都可以参与拖放。拖放操作所需的事件和方法在DragDrop类中定义。所述的UIElement类包含为别名的DragDrop连接事件,从而当所述事件出现在类成员列表的UIElement被继承作为基础元件。与这些事件关联的事件处理程序将连接到基础的DragDrop附加事件,并接收相同的事件数据实例。有关更多信息,请参见UIElement Drop事件。
数据传输
拖放操作类似于复制和粘贴操作,该操作用于将数据从一个对象传输到另一个对象。两种操作都需要:
- 提供数据的源对象。
- 一种临时存储传输数据的方法。
- 接收数据的目标对象。
在复制和粘贴操作中,系统剪贴板用于临时存储传输的数据。在拖放操作中,一个对象用于存储数据。
拖动源通过调用静态DragDrop DoDragDrop方法并将传递的数据传递给它来启动拖放操作。放置目标应该知道如何从事件中接收到的数据对象中获取所需的信息。
拖放操作的源和目标是UI元素。但是,实际传输的数据通常没有可视化表示。您可以编写代码以直观表示所拖动的数据,例如在Windows资源管理器中拖动文件时发生的情况。默认情况下,通过更改光标向用户提供反馈,以表示拖放操作将对数据产生的影响,例如数据将被移动还是复制。
拖放效果
拖放操作可能会对传输的数据产生不同的影响。例如,您可以复制数据或移动数据。NoesisGUI定义了一个DragDropEffects枚举,可用于指定拖放操作的效果。
在拖动源中,可以在DoDragDrop方法中指定源将允许的效果。在放置目标中,可以在DragEventArgs类的Effects属性中指定目标预期的效果。当放置目标在DragOver事件中指定其预期效果时,该信息将在GiveFeedback事件中传递回拖动源。拖动源使用此信息来通知用户放置目标打算对数据产生什么影响。当数据被删除,放置目标指定了其实际效果下降事件。完成放置操作后,该信息将传递回提供给DoDragDrop方法的回调上的拖动源。如果放置目标返回的效果不在allowedEffects的拖动源列表中,则将取消拖放操作,而不会发生任何数据传输。
重要的是要记住,在NoesisGUI中,DragDropEffects值仅用于提供有关拖放操作效果的拖动源和放置目标之间的通信。拖放操作的实际效果取决于您在应用程序中编写适当的代码。
例如,放置目标可能指定放置数据对其的影响是移动数据。但是,要移动数据,必须将其同时添加到目标元素和从源元素中删除。源元素可能表明它允许移动数据,但是如果您不提供从源元素中删除数据的代码,则最终结果将是复制而不移动数据。
拖放事件
拖放操作支持事件驱动的模型。拖动源和放置目标都使用一组标准事件来处理拖放操作。下表总结了标准的拖放事件。这些是DragDrop类上的附加事件。
拖动源事件
<colgroup style="box-sizing: inherit;"><col width="8%" style="box-sizing: inherit;"><col width="92%" style="box-sizing: inherit;"></colgroup>
事件 | 摘要 |
---|---|
给予反馈 | 此事件在拖放操作期间连续发生,并使拖动源能够向用户提供反馈信息。通常通过更改鼠标指针的外观以指示放置目标允许的效果来提供此反馈。这是一个冒泡事件。 |
QueryContinueDrag | 在拖放操作期间键盘或鼠标按钮状态发生更改时,会发生此事件,并使拖放源根据键/按钮状态取消拖放操作。这是一个冒泡事件。 |
PreviewGiveFeedback | GiveFeedback的隧道版本。 |
PreviewQueryContinueDrag | QueryContinueDrag的隧道版本。 |
放下目标事件
<colgroup style="box-sizing: inherit;"><col width="11%" style="box-sizing: inherit;"><col width="89%" style="box-sizing: inherit;"></colgroup>
事件 | 摘要 |
---|---|
拖动输入 | 将对象拖动到放置目标的边界中时,将发生此事件。这是一个冒泡事件。 |
拖曳离开 | 将对象拖到放置目标的边界之外时,将发生此事件。这是一个冒泡事件。 |
拖曳 | 当在拖放目标的边界内拖动(移动)对象时,此事件连续发生。这是一个冒泡事件。 |
下降 | 将对象放置在放置目标上时,将发生此事件。这是一个冒泡事件。 |
PreviewDragEnter | DragEnter的隧道版本。 |
PreviewDragLeave | 隧道版本的DragLeave。 |
PreviewDragOver | DragOver的隧道版本。 |
PreviewDrop | Drop的隧道版本。 |
要处理对象实例的拖放事件,请添加上表中列出的事件的处理程序。要在类级别处理拖放事件,请重写相应的虚拟OnEvent和OnPreviewEvent方法。
实施拖放
UI元素可以是拖动源,放置目标或两者。要实现基本的拖放,您可以编写代码来启动拖放操作并处理拖放的数据。您可以通过处理可选的拖放事件来增强拖放体验。
要实现基本的拖放,您将完成以下任务:
- 确定将成为拖动源的元素。拖动源可以是任何UIElement。我们建议为此使用行为。
class DragItemBehavior final: public NoesisApp::BehaviorT<Noesis::ContentControl> { };
<ItemsControl ItemsSource="{Binding Inventory}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" Style="{StaticResource Style.InventorySlot}">
<i:Interaction.Behaviors>
<local:DragItemBehavior/>
</i:Interaction.Behaviors>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
- 在拖动源上创建一个事件处理程序,该事件处理程序将启动拖放操作。该事件通常是MouseMove事件。
void DragItemBehavior::OnAttached()
{
Noesis::ContentControl* control = GetAssociatedObject();
control->PreviewMouseMove() += Noesis::MakeDelegate(this, &DragItemBehavior::OnMouseMove);
}
- 在拖动源事件处理程序中,调用DoDragDrop方法以启动拖放操作。在DoDragDrop调用中,指定拖放源,要传输的数据,允许的效果以及要通知放置操作结果的可选回调。
void DragItemBehavior::OnMouseMove(Noesis::BaseComponent* sender, const Noesis::MouseEventArgs& e)
{
if (_mouseClicked)
{
Noesis::ContentControl* control = GetAssociatedObject();
Slot* slot = Noesis::DynamicCast<Slot*>(control->GetContent());
if (slot != nullptr && slot->GetItem() != nullptr)
{
slot->StartDragging();
Noesis::DragDrop::DoDragDrop(control, slot, DragDropEffects_Move,
[](Noesis::DependencyObject* source, Noesis::BaseComponent* data, Noesis::UIElement* target,
const Noesis::Point& dropPoint, uint32_t effects)
{
Slot* slot = (Slot*)data;
slot->EndDragging();
});
}
_mouseClicked = false;
}
}
- 确定将成为放置目标的元素。放置目标可以是任何UIElement。在这里我们也可以使用行为。
class DropItemBehavior final: public NoesisApp::BehaviorT<Noesis::ContentControl> { };
<ItemsControl ItemsSource="{Binding Equipment}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" Style="{StaticResource Style.EquipmentSlot}">
<i:Interaction.Behaviors>
<local:DropItemBehavior/>
</i:Interaction.Behaviors>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
-
在放置目标上,将AllowDrop属性设置为true。
-
在放置目标中,创建一个放置事件处理程序以处理放置的数据。
void DropItemBehavior::OnAttached()
{
Noesis::ContentControl* control = GetAssociatedObject();
control->SetAllowDrop(true);
control->PreviewDrop() += Noesis::MakeDelegate(this, &DropItemBehavior::OnDrop);
}
-
在Drop事件处理程序中,从DragEventArgs中提取数据。
-
在Drop事件处理程序中,使用数据执行所需的拖放操作。
void DropItemBehavior::OnDrop(Noesis::BaseComponent* sender, const Noesis::DragEventArgs& e)
{
Noesis::ContentControl* control = GetAssociatedObject();
Slot* sourceSlot = (Slot*)e.data;
Slot* targetSlot = Noesis::DynamicCast<Slot*>(control->GetContent());
if (targetSlot != nullptr)
{
if (targetSlot->IsDropAllowed())
{
Item* draggedItem = sourceSlot->GetItem();
sourceSlot->SetItem(targetSlot->GetItem());
targetSlot->SetItem(draggedItem);
}
else
{
e.effects = DragDropEffects_None;
}
}
e.handled = true;
}
您可以通过处理可选的拖动源和放置目标事件来增强拖放实现,如以下任务所示:
- 若要传输自定义数据或多个数据项,请创建自己的数据类型以传递给DoDragDrop方法。
- 要在拖动过程中执行其他操作,请在放置目标上处理DragEnter,DragOver和DragLeave事件。
- 若要更改鼠标指针的外观,请处理拖动源上的GiveFeedback事件。
- 若要更改取消拖放操作的方式,请处理拖动源上的QueryContinueDrag事件。
网友评论