识别拖放手势

.NET Multi-platform App UI (.NET MAUI) 拖放手势识别器利用连续手势将项及其相关的数据包从屏幕上的一个位置拖放到另一个位置。 拖放手势可以在单个应用程序中进行,也可以在一个应用程序中开始,然后在另一个应用程序中结束。

拖动源(即启动拖动手势的元素)可以通过填充数据包对象来提供要传输的数据。 当拖放源被释放时,就会发生放置。 然后,放置目标(即拖动源下的元素)会处理数据包。

在应用中启用拖放的过程如下所示:

  1. 启用对元素的拖动,具体方法为将 DragGestureRecognizer 对象添加到它的 GestureRecognizers 集合中。 有关详细信息,请参阅启用拖动
  2. [可选] 生成数据包。 .NET MAUI 自动为图像和文本控件填充数据包,但对于其他内容,则需要构造自己的数据包。 有关详细信息,请参阅生成数据包
  3. 通过将 DropGestureRecognizer 对象添加至元素的 GestureRecognizers 集合中启用对元素的放置。 有关详细信息,请参阅启用放置
  4. [可选] 处理 DropGestureRecognizer.DragOver 事件,以指明放置目标允许执行的操作类型。 有关详细信息,请参阅处理 DragOver 事件
  5. [可选] 处理数据包,以接收放置的内容。 .NET MAUI 将自动从数据包中检索图像和文本数据,但对于其他内容,则需要处理数据包。 有关详细信息,请参阅处理数据包

启用拖动

在 .NET MAUI 中,拖动手势识别是由 DragGestureRecognizer 类提供的。 此类定义了以下属性:

这些属性由 BindableProperty 对象提供支持,表示它们可以是数据绑定的目标,并可以设置样式。

DragGestureRecognizer 类还定义了 DragStartingDropCompleted 事件,这些事件会在 CanDrag 属性为 true 时触发。 当检测到拖动手势时,DragGestureRecognizer 对象就会执行 DragStartingCommand,并调用 DragStarting 事件。 然后,当检测到放置手势完成时,DragGestureRecognizer 对象就会执行 DropCompletedCommand,并调用 DropCompleted 事件。

DragStarting 事件随附的 DragStartingEventArgs 对象定义了以下属性:

  • Cancel 属于 bool 类型,指明是否应取消事件。
  • Data 属于 DataPackage 类型,指明拖动源随附的数据包。 这是只读属性。
  • 类型为 PlatformDragStartingEventArgs?PlatformArgs 表示与事件关联的平台特定参数。

在 Android 上,PlatformDragStartingEventArgs 类定义以下属性:

  • 类型为 ViewSender 表示附加到事件的本机视图。
  • 类型为 MotionEventMotionEvent 表示包含拖放状态信息的事件。

此外,在 Android 上,PlatformDragStartingEventArgs 类定义了以下方法:

  • SetDragShadowBuilder,用于设置拖动开始时要使用的 View.DragShadowBuilder
  • SetClipData,用于设置拖动开始时要使用的 ClipData
  • SetLocalData,用于设置拖动开始时要使用的本地数据。
  • SetDragFlags,用于设置拖动开始时要使用的 DragFlags

例如,使用 SetClipData 方法将 ClipData 与拖动的项关联:

void OnDragStarting(object sender, DragStartingEventArgs e)
{
#if ANDROID
    string content = "insert your content here";
    e.PlatformArgs.SetClipData(Android.Content.ClipData.NewPlainText("Drag data", content));
#endif
}

DropCompleted 事件附带的 DropCompletedEventArgs 对象定义一个类型为 PlatformDropCompletedEventArgs?PlatformArgs 属性,该属性表示与事件关联的平台特定参数。

在 Android 上,PlatformDropCompletedEventArgs 类定义以下属性:

  • 类型为 ViewSender 表示附加到事件的本机视图。
  • 类型为 DragEventDragEvent 表示在拖放操作期间的不同时间发送的事件。

下面的 XAML 示例展示了附加到 ImageDragGestureRecognizer

<Image Source="monkeyface.png">
    <Image.GestureRecognizers>
        <DragGestureRecognizer />
    </Image.GestureRecognizers>
</Image>

在此示例中,可以对 Image 启动拖动手势。

提示

拖动手势可通过先长按再拖动来启动。

生成数据包

对于以下控件,当拖动开始时,.NET MAUI 将为你自动生成数据包:

下表显示了当对文本控件启动拖动手势时读取的属性和尝试执行的任何转换:

控件 属性 转换
CheckBox IsChecked bool 已转换为 string
DatePicker Date DateTime 已转换为 string
Editor Text
Entry Text
Label Text
RadioButton IsChecked bool 已转换为 string
Switch IsToggled bool 已转换为 string
TimePicker Time TimeSpan 已转换为 string

对于除文本和图像以外的内容,你需要自行生成数据包。

数据包由 DataPackage 类表示,此类定义了以下属性:

DataPackagePropertySet 类表示存储为 Dictionary<string,object> 的属性包。 若要了解 DataPackageView 类,请参阅处理数据包

存储图像或文本数据

通过在 DataPackage.ImageDataPackage.Text 属性中存储图像或文本数据,可以将这些数据与拖动源关联。 可以在 DragStarting 事件的处理程序中添加数据。

下面的 XAML 示例展示了为 DragStarting 事件注册处理程序的 DragGestureRecognizer

<Path Stroke="Black"
      StrokeThickness="4">
    <Path.GestureRecognizers>
        <DragGestureRecognizer DragStarting="OnDragStarting" />
    </Path.GestureRecognizers>
    <Path.Data>
        <!-- PathGeometry goes here -->
    </Path.Data>
</Path>

在此示例中,DragGestureRecognizer 附加到 Path 对象。 在 Path 上检测到拖动手势时,将引发 DragStarting 事件,该事件将执行 OnDragStarting 事件处理程序:

void OnDragStarting(object sender, DragStartingEventArgs e)
{
    e.Data.Text = "My text data goes here";
}

DragStarting 事件随附的 DragStartingEventArgs 对象有类型为 DataPackageData 属性。 在此示例中,DataPackage 对象的 Text 属性设置为 string。 然后,可以在放置时访问 DataPackage 来检索 string

将数据存储在属性包中

任何数据(包括图像和文本)都可以通过将数据存储在 DataPackage.Properties 集合中来与拖动源关联。 可以在 DragStarting 事件的处理程序中添加数据。

下面的 XAML 示例展示了为 DragStarting 事件注册处理程序的 DragGestureRecognizer

<Rectangle Stroke="Red"
           Fill="DarkBlue"
           StrokeThickness="4"
           HeightRequest="200"
           WidthRequest="200">
    <Rectangle.GestureRecognizers>
        <DragGestureRecognizer DragStarting="OnDragStarting" />
    </Rectangle.GestureRecognizers>
</Rectangle>

在此示例中,DragGestureRecognizer 附加到 Rectangle 对象。 在 Rectangle 上检测到拖动手势时,将引发 DragStarting 事件,该事件将执行 OnDragStarting 事件处理程序:

void OnDragStarting(object sender, DragStartingEventArgs e)
{
    Shape shape = (sender as Element).Parent as Shape;
    e.Data.Properties.Add("Square", new Square(shape.Width, shape.Height));
}

DragStarting 事件随附的 DragStartingEventArgs 对象有类型为 DataPackageData 属性。 可以通过修改 DataPackage 对象的 Properties 集合(即 Dictionary<string, object> 集合)来存储所需的任何数据。 在此示例中,对 Properties 字典做了修改,以存储根据“Square”键表示 Rectangle 大小的 Square 对象。

启用放置

在 .NET MAUI 中,放置手势识别由 DropGestureRecognizer 类提供。 此类定义了以下属性:

这些属性由 BindableProperty 对象提供支持,表示它们可以是数据绑定的目标,并可以设置样式。

DropGestureRecognizer 类还定义了 DragOverDragLeaveDrop 事件,这些事件会在 AllowDrop 属性为 true 时触发。 当在放置目标上识别拖动源时,DropGestureRecognizer 就会执行 DragOverCommand,并调用 DragOver 事件。 然后,如果将拖动源拖至放置目标,DropGestureRecognizer 将执行 DragLeaveCommand 并调用 DragLeave 事件。 最后,当在放置目标上识别放置手势时,DropGestureRecognizer 就会执行 DropCommand,并调用 Drop 事件。

随附 DragOverDragLeave 事件的 DragEventArgs 类定义了以下属性:

在 Android 上,PlatformDragEventArgs 类定义以下属性:

  • 类型为 ViewSender 表示附加到事件的本机视图。
  • 类型为 DragEventDragEvent 表示在拖放操作期间的不同时间发送的事件。

若要了解 DataPackageOperation 枚举,请参阅处理 DragOver 事件

Drop 事件随附的 DropEventArgs 类定义了以下属性:

  • Data 属于 DataPackageView 类型,它是数据包的只读版本。
  • Handled,类型为 bool,指示事件处理程序是否已经处理事件,或者 .NET MAUI 是否应继续自行处理。
  • PlatformArgs,类型为 PlatformDropEventArgs?,表示与事件关联的平台特定参数。

在 Android 上,PlatformDropEventArgs 类定义以下属性:

  • 类型为 ViewSender 表示附加到事件的本机视图。
  • 类型为 DragEventDragEvent 表示在拖放操作期间的不同时间发送的事件。

下面的 XAML 示例展示了附加到 ImageDropGestureRecognizer

<Image BackgroundColor="Silver"
       HeightRequest="300"
       WidthRequest="250">
    <Image.GestureRecognizers>
        <DropGestureRecognizer />
    </Image.GestureRecognizers>
</Image>

在此示例中,将拖动源放到 Image 放置目标上时,如果拖动源为 ImageSource,则它将被复制到放置目标。 .NET MAUI 自动将拖动的图像和文本复制到兼容的放置目标。

处理 DragOver 事件

可以视需要选择处理 DropGestureRecognizer.DragOver 事件,以指明放置目标允许执行的操作类型。 可以通过在随附 DragOver 事件的 DragEventArgs 对象上设置类型 DataPackageOperationAcceptedOperation 属性指示允许的操作。

DataPackageOperation 枚举定义以下成员:

  • None:指明不执行任何操作。
  • Copy:指明将拖动源内容复制到放置目标。

重要说明

DragEventArgs 对象创建时,AcceptedOperation 属性默认为 DataPackageOperation.Copy

下面的 XAML 示例展示了为 DragOver 事件注册处理程序的 DropGestureRecognizer

<Image BackgroundColor="Silver"
       HeightRequest="300"
       WidthRequest="250">
    <Image.GestureRecognizers>
        <DropGestureRecognizer DragOver="OnDragOver" />
    </Image.GestureRecognizers>
</Image>

在此示例中,DropGestureRecognizer 附加到 Image 对象。 将拖动源拖动到放置目标上但尚未放置时,将触发 DragOver 事件,此事件将执行 OnDragOver 事件处理程序:

void OnDragOver(object sender, DragEventArgs e)
{
    e.AcceptedOperation = DataPackageOperation.None;
}

在此示例中,DragEventArgs 对象的 AcceptedOperation 属性设置为 DataPackageOperation.None。 此值可确保在将拖动源放置到放置目标上时不执行任何操作。

处理数据包

在放置目标上释放拖动源时,将引发 Drop 事件。 将拖动源放置到以下控件上时,.NET MAUI 会自动尝试从数据包中检索数据:

下表显示了将基于文本的拖动源放置到文本控件上时要设置的属性和尝试执行的任何转换:

控件 属性 转换
CheckBox IsChecked string 转换为 bool
DatePicker Date string 转换为 DateTime
Editor Text
Entry Text
Label Text
RadioButton IsChecked string 转换为 bool
Switch IsToggled string 转换为 bool
TimePicker Time string 转换为 TimeSpan

对于除文本和图像以外的内容,你需要自行处理数据包。

Drop 事件随附的 DropEventArgs 类定义了类型为 DataPackageViewData 属性。 此属性表示数据包的只读版本。

检索图像或文本数据

使用 DataPackageView 类中定义的方法,可以在 Drop 事件的处理程序中从数据包中检索图像或文本数据。

DataPackageView 类包含 GetImageAsyncGetTextAsync 方法。 GetImageAsync 方法从数据包中检索存储在 DataPackage.Image 属性中的图像,并返回 Task<ImageSource>。 同样,GetTextAsync 方法从数据包中检索存储在 DataPackage.Text 属性中的文本,并返回 Task<string>

下面的示例展示了从 Path 的数据包中检索文本的 Drop 事件处理程序:

async void OnDrop(object sender, DropEventArgs e)
{
    string text = await e.Data.GetTextAsync();

    // Perform logic to take action based on the text value.
}

在此示例中,文本数据是使用 GetTextAsync 方法从数据包中检索的。 然后,可以执行基于文本值的操作。

从属性包中检索数据

通过访问数据包的 Properties 集合,可以在 Drop 事件的处理程序中从数据包中检索任何数据。

DataPackageView 类定义了类型为 DataPackagePropertySetViewProperties 属性。 DataPackagePropertySetView 类表示存储为 Dictionary<string, object> 的只读属性包。

下面的示例展示了从 Rectangle 的数据包的属性包中检索数据的 Drop 事件处理程序:

void OnDrop(object sender, DropEventArgs e)
{
    Square square = (Square)e.Data.Properties["Square"];

    // Perform logic to take action based on retrieved value.
}

在此示例中,Square 对象是通过指定“Square”字典键从数据包的属性包中检索的。 然后,可以执行基于检索到的值的操作。

获取手势位置

可以通过对 DragEventArgsDragStartingEventArgsDropEventArgs 对象调用 GetPosition 方法获取拖放手势发生的位置。 GetPosition 方法接受 Element? 参数,并将位置作为 Point? 对象返回:

void OnDragStarting(object sender, DragStartingEventArgs e)
{
    // Position relative to screen
    Point? screenPosition = e.GetPosition(null);

    // Position relative to specified element
    Point? relativeToImagePosition = e.GetPosition(image);
}

Element? 参数定义获取位置应相对于的元素。 提供一个 null 值作为此参数意味着 GetPosition 方法返回一个 Point? 对象,该对象定义相对于屏幕的拖放手势位置。