Skip to content

Instantly share code, notes, and snippets.

@softlion
Last active February 6, 2022 10:58
Show Gist options
  • Save softlion/5a845180c51b90c8624187273cef9193 to your computer and use it in GitHub Desktop.
Save softlion/5a845180c51b90c8624187273cef9193 to your computer and use it in GitHub Desktop.
Xamarin Forms get absolute bounds of a control and display a menu at this location
namespace Vapolia
{
public interface IScreenLocation
{
PointF GetCoordinates(VisualElement view);
}
}
//using Vapolia.UserInteraction
public ICommand OpenMenuCommand => MakeCommand<VisualElement>(async sourceView =>
{
var bounds = ThisControlExtension.GetAbsoluteBounds(sourceView);
var action = await UserInteraction.Menu(CancellationToken.None, true, bounds, null, null, -1, "Cancel", "Remove", "Rename", "Copy", "Open");
//if(action == 1) ...
});
<ContentPage ... x:Name="ThePage">
<Button Text="My Button"
Command="{Binding SomeCommand,Source={x:Reference ThePage}}"
CommandParameter="{vapolia:ThisControl}" />
</ContentPage>
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly:Dependency(typeof(Vapolia.Droid.ScreenLocation))]
namespace Vapolia.Droid
{
public class ScreenLocation : IScreenLocation
{
public System.Drawing.PointF GetCoordinates(VisualElement element)
{
var renderer = Platform.GetRenderer(element);
var nativeView = renderer.View;
var density = nativeView.Context!.Resources!.DisplayMetrics!.Density;
var locationWindow = new int[2];
nativeView.GetLocationInWindow(locationWindow);
var locationOfRootWindow = new int[2];
nativeView.RootView.FindViewById(Android.Resource.Id.Content).GetLocationInWindow(locationOfRootWindow);
return new System.Drawing.PointF(locationWindow[0] / density, (locationWindow[1]-locationOfRootWindow[1]) / density);
}
}
}
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly:Dependency(typeof(Vapolia.iOS.ScreenLocation))]
namespace Vapolia.iOS
{
public class ScreenLocation : IScreenLocation
{
public System.Drawing.PointF GetCoordinates(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((float)rect.X, (float)rect.Y);
}
}
}
namespace Vapolia
{
/// <summary>
/// Sample Usage:
/// <Button Text="Click Me" Command="{Binding SomeCommand}" CommandParameter="{Binding Source={vapolia:ThisControl}}" />
/// </summary>
public sealed class ThisControlExtension : IMarkupExtension<VisualElement>
{
public VisualElement ProvideValue(IServiceProvider sp)
{
var target = (IProvideValueTarget)sp.GetService(typeof(IProvideValueTarget));
return (VisualElement)target.TargetObject;
}
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) => ProvideValue(serviceProvider);
public static RectangleF GetAbsoluteBounds(VisualElement view)
{
var screenLocation = DependencyService.Get<IScreenLocation>();
var pos = screenLocation.GetCoordinates(view);
return new(pos.X, pos.Y, (float)view.Width, (float)view.Height);
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment