添加拖放手势识别器

借助拖放手势,可以使用连续手势将项及其相关的数据包从屏幕上的一个位置拖放到另一个位置。 拖放手势可以在单个应用程序中进行,也可以在一个应用程序中开始,然后在另一个应用程序中结束。

重要

iOS、Android 和通用 Windows 平台 (UWP) 都支持识别拖放手势。 不过,在 iOS 上,平台版本不得低于 iOS 11。

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

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

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

注意

暂不支持将项拖放到 CollectionView 和从中拖动项。

启用拖动

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

  • CanDrag 属于 bool 类型,指明手势识别器附加到的元素能否为拖动源。 此属性的默认值为 true
  • DragStartingCommand 属于 ICommand 类型,在第一次识别拖动手势时执行。
  • DragStartingCommandParameter 属于 object 类型,是传递给 DragStartingCommand 的参数。
  • DropCompletedCommand 属于 ICommand 类型,在放置拖动源时执行。
  • DropCompletedCommandParameter 属于 object 类型,是传递给 DropCompletedCommand 的参数。

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

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

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

  • Handled:类型为 bool,指明是事件处理程序已经处理了事件,还是 Xamarin.Forms 应继续执行自己的处理。
  • Cancel 属于 bool 类型,指明是否应取消事件。
  • Data 属于 DataPackage 类型,指明拖动源随附的数据包。 这是只读属性。

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

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

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

提示

在 iOS、Android 和 UWP 上,拖动手势是通过先长按再拖动来启动的。

生成数据包

Xamarin.Forms(在拖动手势启动时)自动为以下控件生成数据包:

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

控件 属性 转换
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 类表示,此类定义了以下属性:

  • Properties 属于 DataPackagePropertySet 类型,它是组成 DataPackage 中所包含数据的属性的集合。 此为只读属性。
  • Image 属于 ImageSource 类型,它是 DataPackage 中包含的图像。
  • Text 属于 string 类型,它是 DataPackage 中包含的文本。
  • View 属于 DataPackageView 类型,它是 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 对象。

启用放置

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

  • AllowDrop 属于 bool 类型,指明手势识别器附加到的元素能否成为放置目标。 此属性的默认值为 true
  • DragOverCommand 属于 ICommand 类型,在拖动源被拖动到放置目标上时执行。
  • DragOverCommandParameter 属于 object 类型,是传递给 DragOverCommand 的参数。
  • DragLeaveCommand 属于 ICommand 类型,在拖动源被拖至放置目标上时执行。
  • DragLeaveCommandParameter 属于 object 类型,是传递给 DragLeaveCommand 的参数。
  • DropCommand 属于 ICommand 类型,在拖动源被放置到放置目标上时执行。
  • DropCommandParameter 属于 object 类型,是传递给 DropCommand 的参数。

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

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

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

  • Data 属于 DataPackage 类型,它包含与拖动源关联的数据。 此属性为只读。
  • AcceptedOperation 属于 DataPackageOperation 类型,指定放置目标允许执行的操作。

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

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

  • Data 属于 DataPackageView 类型,它是数据包的只读版本。
  • Handled:类型为 bool,指明是事件处理程序已经处理了事件,还是 Xamarin.Forms 应继续执行自己的处理。

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

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

在此示例中,当拖动源被放置到 Image 放置目标上时,拖动源就会被复制到放置目标,但前提是拖动源是 ImageSource。 这是因为 Xamarin.Forms 自动将拖动的图像和文本复制到兼容的放置目标。

处理 DragOver 事件

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

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 事件。 当发生这种情况时,如果拖动源被放置到以下控件上,Xamarin.Forms 就会自动尝试从数据包中检索数据:

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

控制 属性 转换
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”字典键从数据包的属性包中检索的。 然后,可以执行基于检索到的值的操作。