Drag and drop in Xamarin.Forms
Tags: mobile, net, xamarin, xamarinios
Published: 2020-05-20 11:35:35

---
title: Drag and drop in Xamarin.Forms
published: true
date: 2019-10-10 12:00:00 UTC
tags: Mobile,.Net,Xamarin,Xamarin.iOS
canonical_url: https://blog.francois.raminosona.com/drag-and-drop-in-xamarin-forms/
cover_image: https://images.unsplash.com/reserve/wi9yf7kTQxCNeY72cCY6_Images%20of%20Jenny%20Lace%20Plasticity%20Publish%20(4%20of%2025).jpg?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ
---

In this article I will be showing you how to add a Drag and Drop interaction in your Xamarin.Forms app. I won't use any native component or Custom Renderer on purpose, many of these solutions are available elsewhere (see **_[Drag and Drop solutions for Xamarin](https://blog.francois.raminosona.com/drag-and-drop-solutions-in-xamarin)_**). I'll be using PanGestureRecognizer, TranslateTo and some basic maths.

All the code I'm about to explain here is available on my [**Developer Playground on Github**](https://github.com/framinosona/Xamarin-Developer-Sample/tree/master/DeveloperSample.Core/Pages/DragAndDrop/Sample3). Feel free to check it out.

## [1- PanGestureRecognizer](https://github.com/framinosona/Xamarin-Developer-Sample/tree/master/DeveloperSample.Core/Pages/DragAndDrop/Sample1)

The Sample 1 page let's you play with the PanGestureRecognizer and the data it sends. A pan gesture is a gesture where the user puts at least one finger down and drags it from a point A to a point B then releases it. This gesture fits perfectly what we want to do.

<iframe width="459" height="344" src="https://www.youtube.com/embed/uKgsL4X-9dA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

![Drag and drop in Xamarin.Forms (Fixed for Android)](https://blog.francois.raminosona.com/content/images/2019/06/IMG_9410-squashed.jpeg)

[This sample](https://github.com/framinosona/Xamarin-Developer-Sample/tree/master/DeveloperSample.Core/Pages/DragAndDrop/Sample1) shows you that :

- _PanUpdatedEventArgs. **TotalX** _ and _PanUpdatedEventArgs. **TotalY** _ are delta distances between start and end of the pan gesture
- If you initiate 2 gestures at the same time the _PanUpdatedEventArgs. **GestureId** _ might be the same, **do not use as ID**
- _PanUpdatedEventArgs. **StatusType** _ has one of the following values : Canceled, Completed, Running or Started
- When 2 views are overlapping, with each having a gesture recognizer, the top level one captures the gesture and the bottom one doesn't

## [2- Moving the view around](https://github.com/framinosona/Xamarin-Developer-Sample/tree/master/DeveloperSample.Core/Pages/DragAndDrop/Sample2)

### 2.1- View.TranslateTo(X,Y);

Now that we know how to read the information from a PanGesture, we need to move the drag and droppable view accordingly. For this I use TranslateTo() because it only moves the interface, the element itself thinks he doesn't move. No need to "Redraw" or call "InvalidateLayout".

<iframe width="459" height="344" src="https://www.youtube.com/embed/aXXWo-86SRo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

![Drag and drop in Xamarin.Forms (Fixed for Android)](https://blog.francois.raminosona.com/content/images/2019/06/Sample2-2.gif)

Here you can play with the duration of the animation, in milliseconds. If you want to add a delay or make the ui fade out when the drag and drop ends, this is the place.

### 2.2- GetScreenCoordinates();

We want to be able to know where our view is located in the screen. Since we don't actually move it we need a way to calculate these coordinates. We need many values to calculate that :

- Distance from the _Top of the screen_ to the _Top of the view_ before pan 
- Distance from the _Left of the screen_ to the _Left of the view_ before pan
- Pan distance horizontally : **e.TotalX**
- Pan distance vertically : **e.TotalY**
- Distance from the _Left of the view_ to the _Center of the view_ : **Width / 2**
- Distance from the _Top of the view_ to the _Center of the view_ : **Height / 2**

To get the first two, I found a comment from Dan Meier ([@FactoryOptimizr](https://twitter.com/factoryoptimizr)) on the [Xamarin's forum](https://forums.xamarin.com/discussion/66386/how-to-get-the-coordinates-where-there-is-a-control-on-the-screen) :

```
/// <summary>
/// Gets the screen coordinates from top left corner.
/// </summary>
/// <returns>The screen coordinates.</returns>
/// <param name="view">View.</param>
public static (double X, double Y) GetScreenCoordinates(this VisualElement view)
{
    // A view's default X- and Y-coordinates are LOCAL with respect to the boundaries of its parent,
    // and NOT with respect to the screen. This method calculates the SCREEN coordinates of a view.
    // The coordinates returned refer to the top left corner of the view.

    // Initialize with the view's "local" coordinates with respect to its parent
    double screenCoordinateX = view.X;
    double screenCoordinateY = view.Y;

    // Get the view's parent (if it has one...)
    if (view.Parent.GetType() != typeof(App))
    {
        VisualElement parent = (VisualElement) view.Parent;
        
        // Loop through all parents
        while (parent != null)
        {
            // Add in the coordinates of the parent with respect to ITS parent
            screenCoordinateX += parent.X;
            screenCoordinateY += parent.Y;

            // If the parent of this parent isn't the app itself, get the parent's parent.
            if (parent.Parent.GetType() == typeof(App))
                parent = null;
            else
                parent = (VisualElement) parent.Parent;
        }
    }

    // Return the final coordinates...which are the global SCREEN coordinates of the view
    return (screenCoordinateX, screenCoordinateY);
}
```

Thank you for that Dan ! 😉

> _[[Check out this file on Github]](https://github.com/framinosona/Xamarin-Developer-Sample/blob/master/DeveloperSample.Core/Helpers/ViewExtensions.cs)_

This allows us to calculate the screen coordinates :

```
var screenCoordinates = this.GetScreenCoordinates();
var xPosition = screenCoordinates.X + e.TotalX + Width / 2;
var yPosition = screenCoordinates.Y + e.TotalY + Height / 2;
```

## [3- Bringing it all together](https://github.com/framinosona/Xamarin-Developer-Sample/tree/master/DeveloperSample.Core/Pages/DragAndDrop/Sample3)

A full Drag and Drop interaction requires events to be sent from the moving views to one or many receiving views.

### 3.1- Receiving View

The receiving view will be the view reacting to some other view drag and dropping. I made _IDragAndDropHoverableView_ and _IDragAndDropReceivingView_ for that :

```
public interface IDragAndDropHoverableView
{
    void OnHovered(List<IDragAndDropMovingView> views);
}

public interface IDragAndDropReceivingView
{
    void OnDropReceived(IDragAndDropMovingView view);
}
```

Note that it can be hovered by many views at once, hence the _List<>_ of views in the parameters. For Drop on the other hand I want to take care of drops separately.

### 3.2- Moving View

The moving view is the view that ... is moving! From that view we need to have the position of the centre (X,Y) available :

```
public interface IDragAndDropMovingView
{
    double ScreenX { get; set; }
    double ScreenY { get; set; }
}
```

To initialise that view I decided to make it an extension method, so it can be called from different types of moving views. It is useful if you want to drag and drop an image, a button, a video, a frame ... anything.

```
public class DragAndDropSample3MovingView : Frame, IDragAndDropMovingView
{
    public double ScreenX { get; set; }
    public double ScreenY { get; set; }

    protected override void OnParentSet()
    {
        base.OnParentSet();
        this.InitializeDragAndDrop();
    }
}
```

### 3.3- Drag and drop Container

In order to manage all this we need a container. In order to know chat can interact with what. For this reason I added the function _GetContainer()_ that goes through all parent views looking for an element of type _IDragAndDropContainer_ of, if none is found, a _Page_.

```
private static VisualElement GetContainer(this IDragAndDropMovingView view)
{
    if (!(view is VisualElement visualElement))
        throw new Exception($"{nameof(IDragAndDropMovingView)} can only be an interface on a {nameof(View)}");
    var dropContainer = visualElement.GetFirstParentOfType<IDragAndDropContainer>();
    if (dropContainer is VisualElement output) return output;
    return visualElement.GetFirstParentOfType<Page>();
}
```

### 3.4- Get all children of type T in View

We need to retrieve all the subviews, children that match the type given in order to get all the receiving or moving views from the container :

```
public static List<T> GetAllChildrenOfType<T>(this Element view)
{
    if (view is ContentPage page) view = page.Content;

    var output = new List<T>();

    if (view is T tview)
        output.Add(tview);

    if (view is Layout layout)
        foreach (var child in layout.Children)
            if (child is Element elementChild)
                output.AddRange(elementChild.GetAllChildrenOfType<T>());

    return output;
}
```

### 3.5- GetFirstParentOfType<T>();

If we put all our drag and drop components inside a container, we need those components to crawl up the view-tree to reach that container. Here is an extension function that does that :

```
public static T GetFirstParentOfType<T>(this Element view)
{
    Element output = view;
    while (output.Parent != null)
    {
        if (output.Parent is T parent)
            return parent;
        output = output.Parent;
    }

    return default;
}
```

### 3.6- UpdateHoverStatuses();

When the moving views are moving, you can see (at the bottom of _PanGestureRecognizer\_PanUpdated_) that they call _UpdateHoverStatuses()_ on the container. This makes the container to recalculate which view is on top of which and to send that information to the receiving views :

```
private static void UpdateHoverStatuses(this VisualElement view)
{
    var allReceivers = view.GetAllChildrenOfType<IDragAndDropHoverableView>();
    var allSenders = view.GetAllChildrenOfType<IDragAndDropMovingView>();

    foreach (var receiver in allReceivers)
    {
        if (!(receiver is VisualElement veReceiver)) continue;
        var x = veReceiver.GetScreenCoordinates().X;
        var y = veReceiver.GetScreenCoordinates().Y;
        var width = veReceiver.Width;
        var height = veReceiver.Height;
        receiver.OnHovered(allSenders.Where(sender => sender.ScreenX >= x &&
                                                        sender.ScreenX <= x + width &&
                                                        sender.ScreenY >= y &&
                                                        sender.ScreenY <= y + height
        ).ToList());
    }
}
```

### 3.7- DragAndDroppableViewDropped(IDragAndDropMovingView);

We also want the container to forward the information to receiving views when a moving view is dropped. Because there might be multiple views underneath I'm sending the info to every receiving view that matches :

```
private static void DragAndDroppableViewDropped(this VisualElement view, IDragAndDropMovingView sender)
{
    var allReceivers = view.GetAllChildrenOfType<IDragAndDropReceivingView>();

    foreach (var receiver in allReceivers)
    {
        if (!(receiver is VisualElement veReceiver)) continue;
        var x = veReceiver.GetScreenCoordinates().X;
        var y = veReceiver.GetScreenCoordinates().Y;
        var width = veReceiver.Width;
        var height = veReceiver.Height;
        if (sender.ScreenX >= x &&
            sender.ScreenX <= x + width &&
            sender.ScreenY >= y &&
            sender.ScreenY <= y + height)
            receiver.OnDropReceived(sender);
    }
}
```

## Result

<iframe width="459" height="344" src="https://www.youtube.com/embed/UzQ2mVTeZF4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

![Drag and drop in Xamarin.Forms (Fixed for Android)](https://blog.francois.raminosona.com/content/images/2019/06/Sample-3-2.gif)

> _[[The source code is available on my Github]](https://github.com/framinosona/Xamarin-Developer-Sample)_

Let me know if it helped you! 😀