教程16:拖放概述

拖放概述

教程数据

本教程概述了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的隧道版本。

要处理对象实例的拖放事件,请添加上表中列出的事件的处理程序。要在类级别处理拖放事件,请重写相应的虚拟OnEventOnPreviewEvent方法。

实施拖放

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方法。
  • 要在拖动过程中执行其他操作,请在放置目标上处理DragEnterDragOverDragLeave事件。
  • 若要更改鼠标指针的外观,请处理拖动源上的GiveFeedback事件。
  • 若要更改取消拖放操作的方式,请处理拖动源上的QueryContinueDrag事件。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容