Xamarin.forms: [Spec] DragGestureRecognizer DropGestureRecognizer

Created on 19 May 2020  ·  9Comments  ·  Source: xamarin/Xamarin.Forms

DragGestureRecognizer

  • Allow users to activate a view for dragging

DropGestureRecognizer

  • Allow users to react to a view being dragged over it and then react

API

VisualStates associated with target drop

<VisualStateGroup x:Name="DragAndDropStates">
            <VisualState x:Name="IsDragging">
            </VisualState>
            <VisualState x:Name="DragOver">
            </VisualState>
</VisualStateGroup>

DropGestureRecognizer

```C#
public class DropGestureRecognizer
{
public bool AllowDrop;
public DragEventArgs DragOver;
public DropEventArgs Drop;
}

### Properties

| API | Description |
| ------------- | ------------- |
| AllowDrop  | This sets up a view to be receiver of a view that's being dropped.  |


### Events

| API | Description |
| ------------- | ------------- |
| DragOver |  This fires when a dragged view is over the receiver view. This fires on the target view |
| Drop | This fires on the target when the user let's go of the view being dragged |


## DragGestureRecognizer

```C#
public class DragGestureRecognizer
{
     public bool CanDrag;
     public DropCompletedEventArgs DropCompleted;
     public DragItemsStartingEventArgs DragStarting;
}

Properties

| API | Description |
| ------------- | ------------- |
| CanDrag | This indicates that this view will be the draggable one. |

Events

| API | Description |
| ------------- | ------------- |
| DropCompleted | This fire once the drop operation has completed |
| DragStarting | This fires once it's detected that the user wants to initiate a drag operation. |

DragState

```C#
public enum DragState
{
Started, Dragging, Completed, Cancelled
}

## DragItemsStartingEventArgs

```C#
public class DragItemsStartingEventArgs
{
     bool Cancel;
     DataPackage Data;
}

Properties

| API | Description |
| ------------- | ------------- |
| Cancel | Don't activate the drag operation. |
| DataPackage | Setup the data package to be dragged. If user doesn't specify anything then we just try our best to infer what it should be based on the source |

DataPackageOperation

```C#
public enum DataPackageOperation
{
None, Copy, Move
}

 ## DragEventArgs

```C#
public class DragEventArgs
{
     DataPackage Data;
     DragState State;
     DataPackageOperation AcceptedOperation
}

Properties

| API | Description |
| ------------- | ------------- |
| Data | This is the data package associated with the view being dragged. If someone drags an image to an image we'll just load the image into the target. If someone drags a label the data will be text and we'll apply the text. If it's just a view then the data will be a reference to that view and users can then just do something with it |
| State | This will indicate the state of the drag operation |
| AcceptedOperation | Indicates the types of actions this element will accept for the given data package. This is how you indicate that a given view will accept the given item. |

DropCompletedEventArgs

```C#
public class DropCompletedEventArgs
{
DataPackageOperation DropResult { get; }
}

### Properties

| API | Description |
| ------------- | ------------- |
| DataPackageOperation | Indicates the type of operation and whether or not it was successful. None means unsuccessful |


 ## DropEventArgs

```C#
public class DropEventArgs
{
     DataPackageView Data;
     bool Handled;
}

Properties

| API | Description |
| ------------- | ------------- |
| Data | Readonly view of the data dropped on the target view |
| Handled | Set this to true if you've handled the data in the handler otherwise Forms will try to process the data itself based on the source/target combination |

DataPackage

This is used to setup the data for transfer

```C#
public class DataPackage
{
public void SetImage(ImageSource source);
public void SetText(string value);
public void SetUri(Uri value);
public DataPackageView GetView()
}

## DataPackageView
This is a read-only copy of the DataPackage object.
This is all the user gets on the drop event to retrieve the data. They can't modify the package

```C#
public class DataPackageView
{
     public ImageSource GetImageAsync();
     public string GetTextAsync();
     public Uri GetUriAsynci();
}

Examples

Basic Xaml Example

<StackLayout>
        <Image
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand">
            <Image.GestureRecognizers>
                <DragGestureRecognizer CanDrag="{Binding EnableDragging}"></DragAndDropGestureRecognizer>
            </Image.GestureRecognizers>
        </Image>
         <StackLayout
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand">
            <StackLayout.GestureRecognizers>
                <DropGestureRecognizer DragOver="DragOverStackLayout"  AllowDrop="true"></DropGestureRecognizer>
            </StackLayout.GestureRecognizers>
        </StackLayout>
        <Image
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand">
            <Image.GestureRecognizers>
                <DropGestureRecognizer DragOver="DragOver" Drop="FiresWhenDropped"  AllowDrop="{Binding EnableDrop}"></DropGestureRecognizer>
            </Image.GestureRecognizers>
        </Image>
    </StackLayout>

Workflow

  • User LongPresses on the drag target
  • DragStarting event fires and the user doesn't do anything

    • Because the user doesn't do anything the ImageSource on the image is set on the data package

  • user drags image over the StackLayout but inside DragOverStackLayout the user doesn't do anything so the SL won't receive the element if the user drops it herre
  • once the finger is over the image DragOver fires and the user sets the accepted operation to something other than None
  • Once the user lifts up their finger FiresWhenDropped fires

    • At this point if the user doesn't do anything than forms will set the ImageSource on the target

    • If they set Handled to True then forms does nothing

CollectionView considerations

First implementation won't have tight integration with CV

API-change DragAndDrop gestures 🖖 high impact proposal-accepted roadmap enhancement ➕

Most helpful comment

What about additional commands to the events?

I really like the specs, just would like to have commands as well as events.

All 9 comments

What about additional commands to the events?

I really like the specs, just would like to have commands as well as events.

@dersia

https://github.com/xamarin/Xamarin.Forms/pull/11537/files#diff-eaca5f8f9dd0e23a11bddbe989c5badaR15

I added commands for all the events

Thank you for the reminder 👍

Thanks for useful things.
Can we see any samples?

Does this support ColletionView ? I saw that first implementations won't have tight integrations with collectionView.. Anyways it was a much needed one and thanks for doing it.

Just tried. Works great.
But that isn't obviously that CanDrag and AllowDrop properties are false by default. Took a little time to figure out that you need to set true for gestures to work. Also, these properties sound differently. I think for most users it is obvious to sound the same like AllowDrag-AllowDrop or CanDrag-CanDrop.
Thanks.

Does this support ColletionView ? I saw that first implementations won't have tight integrations with collectionView.. Anyways it was a much needed one and thanks for doing it.

Works for any view and for items in CollectionView too. But these are simple gestures, they don't know anything about collections. So, scrolling and another interactivity cannot work if you move views in collections.

@IgorKravchenko10

Also, these properties sound differently. I think for most users it is obvious to sound the same like AllowDrag-AllowDrop or CanDrag-CanDrop.

Yea, I've gone around in my head a few times about the naming here. The current naming is basically just a copy paste from the UWP APIs but I'm also not convinced on the current names. One of my thoughts was to just change them both to "Enabled" since our implementation here split these out to separate recognizers.

But that isn't obviously that CanDrag and AllowDrop properties are false by default. Took a little time to figure out that you need to set true for gestures to work.

I've had this thought as well :-) There's a lot of stuff you have to enable at the platform level to even make drag and drop work. For example you have to handle the drag/drop events and then opt in. I setup the implementation to just opt in by default because I'd rather people have to turn things off and just make it all work with minimal API. I feel like defaulting these to true would be a good idea.

Works for any view and for items in CollectionView too. But these are simple gestures, they don't know anything about collections. So, scrolling and another interactivity cannot work if you move views in collections.

Hey that's cool!! I haven't really tested it with CV I just know each native platform has very specific APIs for doing drag and drop with collections and I haven't wired into those yet. I was kind of figuring you could make it work as is but the intention is to have the final implementation tie into the native drag and drop apis related to collections

Thank you for the feedback!!!

I've created an issue here where we can track changes we'd like to make before releasing
https://github.com/xamarin/Xamarin.Forms/issues/11663

Hey that's cool!! I haven't really tested it with CV I just know each native platform has very specific APIs for doing drag and drop with collections and I haven't wired into those yet. I was kind of figuring you could make it work as is but the intention is to have the final implementation tie into the native drag and drop apis related to collections

I have to say that DropGestureRecognizer works on DataTemplate level but not for whole CollectionView.

OnDrop doesn't raise:

<CollectionView ItemsSource="{Binding GroupedItems, Mode=OneWay}">
                        <CollectionView.GestureRecognizers>
                            <DropGestureRecognizer AllowDrop="True" Drop="OnDrop"/>
                        </CollectionView.GestureRecognizers>
                        <CollectionView.ItemTemplate>...</CollectionView.ItemTemplate>
</CollectionView>

OnDrop does raise:

<CollectionView ItemsSource="{Binding GroupedItems, Mode=OneWay}">
                        <CollectionView.ItemTemplate>
                              <DataTemplate>
                                    <Grid>
                                          <Grid.GestureRecognizers>
                                               <DragGestureRecognizer CanDrag="True"/>
                                               <DropGestureRecognizer AllowDrop="True" Drop="OnDrop"/>
                                          </Grid.GestureRecognizers>
                                    </Grid>
                              </DataTemplate>
                        </CollectionView.ItemTemplate>
</CollectionView>

@IgorKravchenko10 yea all the platforms have different delegates and events very specific to lists you have to use to make this all work.
i.e. https://developer.apple.com/documentation/uikit/uicollectionviewdragdelegate

So, I'll need to tie into those :-)

Was this page helpful?
0 / 5 - 0 ratings