Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get coordinates of the selected item in a list view in Xamarin.Forms?

I want to get the coordinates (rectangle bounds: x, y, width and height) of the selected item in the listview relative to the screen (assume the listview fills the whole screen), so that I can create an object at that location and animate it to display some details of the selected item in my Xamarin.Forms app.

listview in xaml:

<ListView ItemTapped="ItemTapped"
    AbsoluteLayout.LayoutFlags="All"
    AbsoluteLayout.LayoutBounds="0.5, 0.5, 1.0, 1.0">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell Height="50">              
                <AbsoluteLayout>
                    <Label Text="{Binding Info}"
                        AbsoluteLayout.LayoutFlags="All"
                        AbsoluteLayout.LayoutBounds="0.1, 0.5, 0.7, 0.5"/>
                </AbsoluteLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

c# code for ItemTapped event:

void ItemTapped(object sender, EventArgs args)
{
    var listView = (ListView)sender; // the listview
    var selectedItem = args.Item; // the selected item

    // need to get selected item coordinates for the animation
    var selectedItemBounds = ...

    ...
}

Eventually I want to create somehting like this in Xamarin.Forms with a listview (the number of objects in the listview varies):

sample listview animation

like image 878
Patrick Avatar asked Mar 13 '18 18:03

Patrick


2 Answers

I've created a dependency you can use to get the absolute position of a VisualElement in iOS and Android. I use it for a similar purpose. We use it to determine the position of a popup to show when tapping in the listview. Works perfectly:

Dependency:

public interface ILocationFetcher
{
    System.Drawing.PointF GetCoordinates(global::Xamarin.Forms.VisualElement view);
}

iOS Implementation:

 class LocationFetcher : ILocationFetcher
 {
    public System.Drawing.PointF GetCoordinates(global::Xamarin.Forms.VisualElement element)
    {
        var renderer = Platform.GetRenderer(element);
        var nativeView = renderer.NativeView;
        var rect = nativeView.Superview.ConvertPointToView(nativeView.Frame.Location, null);
        return new System.Drawing.PointF((int)Math.Round(rect.X), (int)Math.Round(rect.Y));
    }
}

Android Implementation:

class LocationFetcher : ILocationFetcher
{
    public System.Drawing.PointF GetCoordinates(global::Xamarin.Forms.VisualElement element)
    {
        var renderer = Platform.GetRenderer(element);
        var nativeView = renderer.View;
        var location = new int[2];
        var density = nativeView.Context.Resources.DisplayMetrics.Density;

        nativeView.GetLocationOnScreen(location);
        return new System.Drawing.PointF(location[0] / density, location[1] / density);
    }
}

Thanks to @Emil we also have an UWP implementation:

public System.Drawing.PointF GetCoordinates(global::Xamarin.Forms.VisualElement element)
{
    var renderer = Xamarin.Forms.Platform.UWP.Platform.GetRenderer(element);
    var nativeView = renderer.GetNativeElement();
    var element_Visual_Relative = nativeView.TransformToVisual(Window.Current.Content);
    Point point = element_Visual_Relative.TransformPoint(new Point(0, 0));
    return new System.Drawing.PointF((int)Math.Round(point.X), (int)Math.Round(point.Y));
}

usage example:

var locationFetcher = DependencyService.Get<ILocationFetcher>();
var location = locationFetcher.GetCoordinates(myVisualElement);

Be sure to properly register the dependency correctly (see https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/dependency-service/ ) in the android and ios using the dependency attribute. Otherwise the DependencyService.Get will return null.

like image 182
PaulVrugt Avatar answered Oct 22 '22 09:10

PaulVrugt


PaulVrugt's answer perfectly works for IOS and Android. Just to extend his implementation for anyone requires also UWP.

  public System.Drawing.PointF GetCoordinates(global::Xamarin.Forms.VisualElement element)
        {
            var renderer = Xamarin.Forms.Platform.UWP.Platform.GetRenderer(element);
            var nativeView = renderer.GetNativeElement();
            var element_Visual_Relative = nativeView.TransformToVisual(Window.Current.Content);
            Point point = element_Visual_Relative.TransformPoint(new Point(0, 0));
            return new System.Drawing.PointF((int)Math.Round(point.X), (int)Math.Round(point.Y));
        }
like image 2
Emil Avatar answered Oct 22 '22 09:10

Emil