Hi there
I have an image on part of a page that I want a user to be able to position multiple labels over. I thought it would make sense to allow a user to drag the labels to the position on the image they want.
The image is not full screen and there are some other fields on the page so I only what the labels to be able to be moved within the image.
My first attempt was with a Pan gesture:
This allows for an absolute layout that the labels can be on top of. The pan gesture can also be capped.
The pan gesture however seems to be the opposite of what I want in that the pan is actually on the parent (so the image) and not on the label. I can make it so the label is only able to move within the bounds of the image. But instead of dragging the label to move it, you drag the image.
I followed the tutorial from Microsoft as a guide:
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/pan
I have a Pan Container with the gesture.
public PanContainer()
{
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += OnPanUpdated;
GestureRecognizers.Add(panGesture);
}
void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType)
{
case GestureStatus.Running:
// Translate and ensure we don't pan beyond the wrapped user interface element bounds.
//This is the parent Absolute layout bounds size - Content.Width
//Screen width = 960 (Constant) expected
//e.totalX = movement.
//x is current position.
//The current position + the dragged position.
double calculateXValue = x + e.TotalX;
double calculateYValue = x + e.TotalY;
//The second frame is center so divide by 2 to get the bounds.
double capX = Content.Width / 2;
double capY = Content.Height / 2;
double calculateXValueMax = capX;
double calculateXValueMin = capX * -1;
double calculateYValueMax = capY;
double calculateYValueMin = capY * -1;
//Cap the motion to the edges
if (calculateXValue >= calculateXValueMax)
{
calculateXValue = calculateXValueMax;
}
else if(calculateXValue <= calculateXValueMin)
{
calculateXValue = calculateXValueMin;
}
if (calculateYValue >= calculateYValueMax)
{
calculateYValue = calculateYValueMax;
}
else if (calculateYValue <= calculateYValueMin)
{
calculateYValue = calculateYValueMin;
}
Content.TranslationX =
calculateXValue;
//Math.Max(Math.Min(0, x + e.TotalX), -Math.Abs(Content.Width - App.ScreenWidth)); // = -344
Content.TranslationY =
calculateYValue;
//Math.Max(Math.Min(0, y + e.TotalY), -Math.Abs(Content.Height - App.ScreenHeight));
break;
case GestureStatus.Completed:
// Store the translation applied during the pan
x = Content.TranslationX;
y = Content.TranslationY;
break;
}
I have a View that is then the label that I want to be able to click and drag.
<ContentView.Content>
<AbsoluteLayout>
<Frame x:Name="displayName"
VerticalOptions="Center"
HorizontalOptions="Center"
Margin="1"
BackgroundColor="Transparent"
BorderColor="Red"
Padding="1"
AbsoluteLayout.LayoutBounds="0.5,0.5,AutoSize,AutoSize"
AbsoluteLayout.LayoutFlags="PositionProportional">
<StackLayout VerticalOptions="Center">
<Label TextColor="Red" HorizontalOptions="Center" Text="This is some text" />
</StackLayout>
</Frame>
</AbsoluteLayout>
</ContentView.Content>
I then have a "MasterView" that contains the image and the Label view.
<ContentView.Content>
<StackLayout>
<Label Text="Hello Xamarin.Forms!" />
<AbsoluteLayout>
<Frame x:Name="masterDisplay"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Margin="1"
BackgroundColor="Transparent"
BorderColor="Blue"
Padding="1"
AbsoluteLayout.LayoutBounds="0.8,0.5,310,240"
AbsoluteLayout.LayoutFlags="PositionProportional">
<framePan:PanContainer>
<framePan:PanView>
</framePan:PanView>
</framePan:PanContainer>
</Frame>
<Frame x:Name="masterDisplay2"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Margin="1"
BackgroundColor="Transparent"
BorderColor="Blue"
Padding="1"
AbsoluteLayout.LayoutBounds="0.2,0.5,310,240"
AbsoluteLayout.LayoutFlags="PositionProportional">
<framePan:PanContainer>
<framePan:PanView>
</framePan:PanView>
</framePan:PanContainer>
</Frame>
</AbsoluteLayout>
</StackLayout>
</ContentView.Content>
In my demo I am just using a frame with some dimensions where the image would go for simplicity.
I have tried several different ways to cap and move the text within the image and none are quite right. I can get the label to stay within the bounds of the image (frame). But the movement is on the frame not on the Label.
I have done some digging and there is a drag and drop gesture for xamarin forms. This might be a better fit for what I am trying to do however I am unsure. As the action isn't exactly a drag and drop but a move.
I am unsure if there is a way to cap where you can drag and drop to on the screen. My guess is maybe with drag and drop I can drag an item to a new location and then do a transform similar to the pan?
Has anyone tried to do this?
I am unsure if I am looking at the wrong gestures or if what I am doing makes sense.
Many thanks