Restrict Panning in specific border - c#

I need help to bound the path or canvas in a boundary. What I want is, when I click the mouse-left-button, holding it and move for panning. It should not move when mouse pointer reach some boundary as BORDER. I'll add code here please help me out
XAML code:
<Border x:Name="OutMoastBorder" Height="820" Width="820" ClipToBounds="True" BorderThickness="2" BorderBrush="Black" Block.IsHyphenationEnabled="True">
<Border x:Name="clipBorder" Height="810" Width="810" BorderThickness="2" BorderBrush="Black" ClipToBounds="True">
<Canvas x:Name="CanvasPanel" Height="800" Width="800" Background="Beige">
</Canvas>
</Border>
</Border>
<Grid>
<Button Content="Original Size" Height="23" Name="btn_Original" Width="75" Click="btn_Original_Click" Margin="4,4,921,973" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="4,59,0,0" Name="txtNoOfZones" VerticalAlignment="Top" Width="120" MaxLength="2" PreviewTextInput="txtNoOfZones_PreviewTextInput" />
<Label Content="Enter a number below for No. of Zones" Height="28" HorizontalAlignment="Left" Margin="4,33,0,0" Name="label1" VerticalAlignment="Top" Width="220" FontFamily="Vijaya" FontSize="15" FontWeight="Bold" FontStyle="Normal" />
<Button Content="Zones" Height="23" HorizontalAlignment="Left" Margin="130,58,0,0" Name="btnNoOfZones" VerticalAlignment="Top" Width="41" Click="btnNoOfZones_Click" />
</Grid>
</Grid>
Code behind for zooming and panning:
void Zoom_MouseWheel(object sender, MouseWheelEventArgs e)
{
Point p = e.MouseDevice.GetPosition(((Path)sender));
Matrix m = CanvasPanel.RenderTransform.Value;
if (e.Delta > 0)
m.ScaleAtPrepend(1.1, 1.1, p.X, p.Y);
else
m.ScaleAtPrepend(1 / 1.1, 1 / 1.1, p.X, p.Y);
CanvasPanel.RenderTransform = new MatrixTransform(m);
// CanvasPanel.RenderTransformOrigin = new Point(0.5, 0.5);
}
private Point origin;
private Point start;
void Pan_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (((Path)sender).IsMouseCaptured) return;
((Path)sender).CaptureMouse();
start = e.GetPosition(clipBorder);
origin.X = CanvasPanel.RenderTransform.Value.OffsetX;
origin.Y = CanvasPanel.RenderTransform.Value.OffsetY;
}
void Pan_MouseLeftBtnUp(object sender, MouseButtonEventArgs e)
{
((Path)sender).ReleaseMouseCapture();
}
void Pan_MouseMove(object sender, MouseEventArgs e)
{
if (!((Path)sender).IsMouseCaptured) return;
Point p = e.MouseDevice.GetPosition(clipBorder);
Matrix m = CanvasPanel.RenderTransform.Value;
m.OffsetX = origin.X + (p.X - start.X);
m.OffsetY = origin.Y + (p.Y - start.Y);
CanvasPanel.RenderTransform = new MatrixTransform(m);
}
private const int NoOfSectors = 180;
private const int NoOfZones = 5;
void OnLoaded(object sender, RoutedEventArgs args)
{
const double PIES = 2 * Math.PI / NoOfSectors;
Point center = new Point(CanvasPanel.ActualWidth / 2, CanvasPanel.ActualHeight / 2); // center (x,y) Co-ordinates to get center of canvas
double radius = 0.1 * Math.Min(CanvasPanel.ActualWidth, CanvasPanel.ActualHeight);
for (int s = 0; s <= NoOfSectors; s++)
{
for (int z = 1; z <= NoOfZones; z++)
{
Path path = new Path();
PathGeometry pathGeo = new PathGeometry();
PathFigure pathFig = new PathFigure();
double radians = 2 * Math.PI * s / NoOfSectors;
double outerRadius = radius * z;
double innerRadius = radius * ((z - 1));
Size outerArcSize = new Size(outerRadius, outerRadius);
Size innerArcSize = new Size(innerRadius, innerRadius);
Point p1_1st_LineSegment; //------------------------------> Points variable, to store each iterate point in these.
Point p2_1st_ArcSegment;
Point p3_2nd_LineSegment;
Point p4_2nd_ArcSegment;
p1_1st_LineSegment = new Point(center.X + innerRadius * Math.Cos(radians - PIES), center.Y - innerRadius * Math.Sin(radians - PIES)); // point for LINE from Center
p2_1st_ArcSegment = new Point(center.X + innerRadius * Math.Cos(radians), center.Y - innerRadius * Math.Sin(radians)); // point for ARC after the 1st LINE formation
p3_2nd_LineSegment = new Point(center.X + outerRadius * Math.Cos(radians), center.Y - outerRadius * Math.Sin(radians)); // Point for 2nd LINE after forming both LINE abd ARC
p4_2nd_ArcSegment = new Point(center.X + outerRadius * Math.Cos(radians - PIES), center.Y - outerRadius * Math.Sin(radians - PIES)); // Point for 2nd ARC which is Counter-CLockwise that closes a path
pathFig.StartPoint = center;
pathFig.Segments.Add(new LineSegment(p1_1st_LineSegment, true));
pathFig.Segments.Add(new ArcSegment(p2_1st_ArcSegment, innerArcSize, 1, false, SweepDirection.Clockwise, true));
pathFig.Segments.Add(new LineSegment(p3_2nd_LineSegment, true));
pathFig.Segments.Add(new ArcSegment(p4_2nd_ArcSegment, outerArcSize, 1, false, SweepDirection.Counterclockwise, true));
pathFig.IsClosed = false; //false because, path has to be close with ARC, not with LINE
pathFig.IsFilled = true;
pathGeo.Figures.Add(pathFig); // binding data to a Geometry
pathGeo.FillRule = FillRule.Nonzero;
path.Data = pathGeo; // binding whole geometry data to a path
path.Stroke = Brushes.Black;
path.Fill = Brushes.Silver;
path.StrokeThickness = 0.1;
// CanvasPanel.RenderTransformOrigin = new Point(0.5, 0.5); //--------------------> this makes "Canvas" to be Zoom from center
CanvasPanel.Children.Add(path); // binding to a CanvasPanel as a children
path.MouseLeftButtonDown += MouseLeftButtonClick; // calling Mouse-click-event
path.MouseWheel += Zoom_MouseWheel;
path.MouseLeftButtonDown += Pan_MouseLeftButtonDown;
path.MouseLeftButtonUp += Pan_MouseLeftBtnUp;
path.MouseMove += Pan_MouseMove;
}
}
Please help me out.
Regards,
Viswanath.

PLz replace the MouseMove event with this following code in code behind.
void Pan_MouseMove(object sender, MouseEventArgs e)
{
if (!((Path)sender).IsMouseCaptured) return;
Point p = e.MouseDevice.GetPosition(clipBorder);
if (p.X > 0 && p.Y > 0 && p.X < clipBorder.ActualWidth && p.Y < clipBorder.ActualHeight)
{
Matrix m = CanvasPanel.RenderTransform.Value;
m.OffsetX = origin.X + (p.X - start.X);
m.OffsetY = origin.Y + (p.Y - start.Y);
CanvasPanel.RenderTransform = new MatrixTransform(m);
}
}
I have tested, this will surely solve this.
regards,
Viswa

Related

Changing Width of rotated UserControl results in translation

I am trying to create a simple transformation box. I started to add resizing (currently only extending width on the right) but when I change the Width and then rotate, the Y translation messes up. I figure the Y translation must change based on the rotation to keep manipulations looking consistent, but for some reason, I can't fathom how to factor this in.
heres a gif of what I mean
(The following UserControl is a child of a Canvas control)
.xaml
<UserControl
x:Class="TransformBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
PointerPressed="UserControl_PointerPressed"
PointerMoved="UserControl_PointerMoved"
PointerReleased="UserControl_PointerReleased"
Width="200" Height="150">
<UserControl.Resources>
<Style x:Key="ScaleEllipseStyle" TargetType="Ellipse">
<Setter Property="Width" Value="12"/>
<Setter Property="Height" Value="12"/>
<Setter Property="Fill" Value="{StaticResource SystemAccentColor}"/>
</Style>
</UserControl.Resources>
<Grid>
<Rectangle x:Name="RotateRegion" Stroke="Purple" StrokeThickness="20" Margin="-20"/>
<Rectangle x:Name="TranslateRegion" Fill="Transparent" StrokeThickness="2"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="-6,-6,0,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-6,0,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-6,-6,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="-6,0,0,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,-6,0"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="-6,0,0,-6"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,-6"/>
<Ellipse Style="{StaticResource ScaleEllipseStyle}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-6,-6"/>
<Ellipse x:Name="OriginRegion" Width="12" Height="12" Fill="Transparent" Stroke="{StaticResource SystemAccentColor}" StrokeThickness="2"/>
</Grid>
</UserControl>
.xaml.cs
public sealed partial class TransformBox : UserControl
{
private bool isDragging;
private Point prevPosition;
private Point _clickPosition;
private TransformType action;
private double _prevRot = 0;
public float SetWidth = 200;
public float SetHeight = 150;
public Point Origin = new Point(0.5, 0.5);
public float X = 0;
public float Y = 0;
public new float Rotation = 0;
public TransformBox()
{
this.InitializeComponent();
Width = SetWidth;
Height = SetHeight;
}
private void UserControl_PointerPressed(object sender, PointerRoutedEventArgs e)
{
isDragging = true;
_clickPosition = prevPosition = e.GetCurrentPoint(Parent as UIElement).Position;
this.CapturePointer(e.Pointer);
if (e.OriginalSource == TranslateRegion) {
action = TransformType.Translate;
}
else if (e.OriginalSource == RotateRegion)
{
action = TransformType.Rotate;
_prevRot = Math.Atan2(_clickPosition.Y - (Y + Height / 2), _clickPosition.X - (X + Width / 2));
}
else if (e.OriginalSource == OriginRegion)
{
action = TransformType.TransformOrigin;
}
else
{
action = TransformType.Scale;
}
}
private void UserControl_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (isDragging)
{
Point mousePos = e.GetCurrentPoint(Parent as UIElement).Position;
switch (action)
{
case TransformType.Translate:
X += (float) (mousePos.X - prevPosition.X);
Y += (float) (mousePos.Y - prevPosition.Y);
prevPosition = mousePos;
break;
case TransformType.Rotate:
double curRot = Math.Atan2(mousePos.Y - (Y + Height / 2), mousePos.X - (X + Width / 2));
Rotation += (float) (curRot - _prevRot);
_prevRot = curRot;
break;
case TransformType.Scale:
double dist = Math.Sqrt(Math.Pow(mousePos.X - _clickPosition.X, 2) + Math.Pow(mousePos.Y - _clickPosition.Y, 2) * 1.0);
double mouseRot = Math.Atan2(mousePos.Y - _clickPosition.Y, mousePos.X - _clickPosition.X);
double proj = dist * Math.Cos(0 - mouseRot);
Width = SetWidth + proj;
break;
}
Matrix4x4 translate = Matrix4x4.CreateTranslation(new Vector3(X, Y, 0f));
Matrix4x4 rotate = Matrix4x4.CreateRotationZ(Rotation, new Vector3(X + (float) SetWidth / 2, Y + (float) SetHeight / 2, 0f));
TransformMatrix = Matrix4x4.Multiply(translate, rotate);
}
}
private void UserControl_PointerReleased(object sender, PointerRoutedEventArgs e)
{
isDragging = false;
SetWidth = (float) Width;
SetHeight = (float) Height;
this.ReleasePointerCapture(e.Pointer);
}
enum TransformType
{
Translate,
Rotate,
Scale,
TransformOrigin
}
}
Try compensating the drift of the rotation-centre in the PointerReleased event handler, anyway.
private void UserControl_PointerReleased(object sender, PointerRoutedEventArgs e)
{
isDragging = false;
double deltaCentreX = 0.5 * (Width - SetWidth);
double deltaCentreY = 0.5 * (Height - SetHeight);
X -= (float)((1.0 - Math.Cos(Rotation)) * deltaCentreX + Math.Sin(Rotation) * deltaCentreY);
Y -= (float)(-Math.Sin(Rotation) * deltaCentreX + (1.0 - Math.Cos(Rotation)) * deltaCentreY);
SetWidth = (float) Width;
SetHeight = (float) Height;
this.ReleasePointerCapture(e.Pointer);
}

WPF Different behaviour when drawing point versus line

I am attempting to draw a line from the center of an image on a canvas to the mouse's position when the scroll wheel is moved.
I have a function that looks like this:
// e is MouseWheelEventArgs
var position = e.GetPosition(canvas);
var x = Canvas.GetLeft(image) + image.ActualWidth / 2;
var y = Canvas.GetTop(image) + image.ActualHeight / 2;
Ellipse point = new Ellipse
{
Margin = new Thickness(x, y, 0, 0)
};
Line line = new Line
{
X1 = position.X,
Y1 = position.Y,
X2 = x,
Y2 = y
};
canvas.Children.Add(point);
canvas.Children.Add(line);
The point is drawn correctly at the pointer's location, and the line is drawn from the center of the image, but the point the line is drawn to is incorrect. Why is this?
Here is an image to show the location of the point and the line
I would suggest to implement this with Geometries.
With a XAML like this
<Canvas Background="Transparent" MouseWheel="Canvas_MouseWheel">
<Image x:Name="image" Canvas.Left="0" Canvas.Top="0" Source="..."/>
<Path x:Name="line" Stroke="Green" StrokeThickness="2"/>
<Path x:Name="point" Fill="Red"/>
</Canvas>
the event handler could look like this:
private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
var position = e.GetPosition((Canvas)sender);
var center = new Point(
Canvas.GetLeft(image) + image.ActualWidth / 2,
Canvas.GetTop(image) + image.ActualHeight / 2);
line.Data = new LineGeometry(center, position);
point.Data = new EllipseGeometry(position, 3, 3);
}

How to identify the polygone is triangle in wpf

I have 'n' number of polygons as like below.
<Polygon Points="544,245,544,175,568,175,568,175" Stroke="Black" StrokeThickness="1" />
<Polygon Points="2,223,96,223,96,153,96,153" Stroke="Black" StrokeThickness="1" />
<Polygon Points="350,315,350,333,306,333,306,395,306,395" Stroke="Black" StrokeThickness="1" />
<Polygon Points="164,53,160,53,160,51,160,55,160,55" Stroke="Black" StrokeThickness="1" />
<Polygon Points="264,63,264,58,264,68,264,63,267,63,267,60,267,66,267,63,270,63,270,58,270,68,270,68" Stroke="Black" StrokeThickness="1" />
<Polygon Points="8,63,444,63,444,168,444,168" Stroke="Black" StrokeThickness="1" />
<Polygon Points="212,169,212,93,285,93,285,63,285,63" Stroke="Black" StrokeThickness="1" />
<Polygon Points="26,93,127,93,127,148,29,148,29,148" Stroke="Black" StrokeThickness="1" />
<Polygon Points="152,116,152,132,212,132,212,132" Stroke="Black" StrokeThickness="1" />
<Polygon Points="121,316,121,333,70,333,70,366,70,366" Stroke="Black" StrokeThickness="1" />
<Polygon Points="464,395,488,395,488,284,527,284,527,284" Stroke="Black" StrokeThickness="1" />
<Polygon Points="168,63,168,67,180,59,180,67,168,59,168,59" Stroke="Black" StrokeThickness="1" />
<Polygon Points="173,62,173,56,165,56,165,51,175,51,175,61,175,61" Stroke="Black" StrokeThickness="1" />
<Polygon Points="3,285,121,285,121,316,211,316,211,304,211,304" Stroke="Black" StrokeThickness="1" />
Please help me to identify what are the triangles in these polygons
I have tried to identify the vertices like below..
Polygon polygon = new Polygon();
polygon.Points = new System.Windows.Media.PointCollection()
{
new Point(446,134),
new Point(442,134),
new Point(444,140),
new Point(444,140),
};
List<double> verticesPoints = new List<double>();
for (int i = 0; i < polygon.Points.Count - 1; i++)
{
var point1 = polygon.Points[i];
var point2 = polygon.Points[i + 1];
//calculate delta x and delta y between the two points
var deltaX = Math.Pow((point2.X - point1.X), 2);
var deltaY = Math.Pow((point2.Y - point1.Y), 2);
//pythagras theorem for distance
var distance = Math.Sqrt(deltaY + deltaX);
//distance is zero..then same point
if (distance != 0)
{
verticesPoints.Add(distance);
}
}
///Here is the code to calculate angle and consider the triangle
///three vertices then it might be triangle.
if (verticesPoints.Count == 3)
{
///use The Law of Cosines
///cos(C) = a2 + b2 − c2 /2ab
///cos(A) = b2 + c2 − a2 /bc
///cos(B) = c2 + a2 − b2 /ca
var a = ((Math.Pow(verticesPoints[1], 2)) + (Math.Pow(verticesPoints[2], 2)) - (Math.Pow(verticesPoints[0], 2)))
/ (2 * verticesPoints[1] * verticesPoints[2]);
var b = ((Math.Pow(verticesPoints[0], 2)) + (Math.Pow(verticesPoints[2], 2)) - (Math.Pow(verticesPoints[1], 2)))
/ (2 * verticesPoints[0] * verticesPoints[2]);
var c = ((Math.Pow(verticesPoints[0], 2)) + (Math.Pow(verticesPoints[1], 2)) - (Math.Pow(verticesPoints[2], 2)))
/ (2 * verticesPoints[0] * verticesPoints[1]);
///Inverse of cos
var radians1 = Math.Acos(a);
///Convert radian to degree
double degrees1 = (radians1 * 180.0) / Math.PI;
///Inverse of cos
var radians2 = Math.Acos(b);
//Convert radian to degree
double degrees2 = (radians2 * 180.0) / Math.PI;
///Inverse of cos
var radians3 = Math.Acos(c);
///Convert radian to degree
double degrees3 = (radians3 * 180.0) / Math.PI;
var totalDegrees = degrees1 + degrees2 + degrees3;
if (totalDegrees == 180)
{
// Consider triangle
}
}
But above code is not working for <Polygon Points="446,134,442,134,444,140,444,140" Stroke="Black" StrokeThickness="1" /> it is giving only two vertices but it is a triangle and some scenario getting 3 vertices but totalDegrees is not as 180
This code here iterates through the points and calculates the gradient between each of them. If the gradient is the same for two sequential points they must be the same line, so the noOfPoints is not incremented otherwise it is incremented.
The first gradient is stored in firstGradient in order to check if the gradient connecting the last and first point is the same as the gradient between the first and second point.
Polygon polygon = new Polygon();
polygon.Points = new System.Windows.Media.PointCollection()
{
new Point(446,134),
new Point(442,134),
new Point(444,140),
new Point(444,140),
};
List<double> verticesPoints = new List<double>();
double? firstGradient = null;
double? gradient = null;
double? newGradient = null;
int noOfSides = 1;
for (int i = 0; i < polygon.Points.Count - 1; i++)
{
var point1 = polygon.Points[i];
var point2 = polygon.Points[i + 1];
if(point1 == point2) { continue;}
//calculate delta x and delta y between the two points
var deltaX = point2.X - point1.X;
var deltaY = point2.Y - point1.Y;
//calculate gradient
newGradient = (deltaY / deltaX);
if (i == 0)
{
firstGradient = newGradient;
}
if ((gradient != newGradient) && (i != polygon.Points.Count - 2))
{
noOfSides++;
}
else if (i == polygon.Points.Count - 2)
{
if ((gradient != newGradient) && (firstGradient != newGradient)) //This now checks the gradient between the last and first point.
{
point1 = polygon.Points[i+1];
point2 = polygon.Points[0];
if (point1 == point2) { continue; }
//calculate delta x and delta y between the two points
deltaX = point2.X - point1.X;
deltaY = point2.Y - point1.Y;
//calculate gradient
newGradient = (deltaY / deltaX);
if(newGradient != firstGradient)
{
noOfSides++;
}
}
gradient = newGradient;
}
I have solved the above problem using "AForge.NET"
Polygon polygon = new Polygon();
polygon.Points = new PointCollection()
{
new Point(446,134),
new Point(442,134),
new Point(444,140),
new Point(444,140),
};
SimpleShapeChecker shapeChecker = new SimpleShapeChecker();
List<IntPoint> edgePoints = new List<IntPoint>();
List<IntPoint> corners;
for (int i = 0; i <= polygon.Points.Count - 1; i++)
{
edgePoints.Add(new IntPoint((int)polygon.Points[i].X, (int)polygon.Points[i].Y));
}
shapeChecker.MinAcceptableDistortion = 0.2f;
shapeChecker.LengthError = 0;
shapeChecker.AngleError = 5;
shapeChecker.RelativeDistortionLimit = 0;
if (shapeChecker.IsTriangle(edgePoints, out corners))
{
//shape is triangle
}
Need to add below namespace
using AForge;
using AForge.Math.Geometry;
Reference :http://aforgenet.com/articles/shape_checker/

Clipping a low resolution image using Transforms and assigning it to my user control

I have been working on a Windows 8.1 RT app where the user loads an image with Stretch=Uniform.
The image can be as small as possible and as big as possible.
The clipping happens in my user control and my user control appears when I tap/press and hold on the screen/image.
Clipping happens when when I tap and hold and move my finger/mouse around the image that is in the background.
The Problems I am facing are
Whenever the app is loaded for the first time on Emulator , and for the very first time when the tap /clicks and holding is performed , the user control appears on the top left on the screen and then it comes above my clicked/hold area. What I require is it should always appear wherever I click and hold and whenever I click and hold. On releasing the finger/mouse , it should collapse.
I am using center transform. I want the region(the pixel) where my mouse is currently to be displayed exactly in the center of the user control , If i am loading a small resolution image 480*800 or even smaller , the region of my mouse is not coming into the center.
To be more clearer, Imagine I am tapping and holding on the CORNEA of the human eye.The cornea should be displayed in the center of the user control and area above and below should cover the rest of the part.
I don't want my control to go outside my image boundary, if my mouse is at the last pixel of the image , some part of image and some part of background should be displayed.
I need to rotate the control as shown in the video
Find the complete code below.
MainPage.XAML
<Page
x:Class="App78.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Holding="LayoutGrid_Holding"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="MagnifierTip" Visibility="Collapsed" />
</Grid>
</Page>
MAINPAGE.XAML.CS
using System;
using System.Diagnostics;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace App78
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
//DV: If pointer is not in contact we can ignore it
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX = 0;
double offsetY = 0;
double imageScale = 1;
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
//imageScale = 1; - remains
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
//DV: This is probably not need anymore
//MagnifierTip.MagnifierTransform.X = this.pointerX;
//MagnifierTip.MagnifierTransform.Y = this.pointerY;
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
//DV: I haven't tested the Scaling/Zoom
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
//DV: I added a GlobalGrid Transform which translates every children
MagnifierTip.MagnifierTransformGrid.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTransformGrid.Y = this.pointerY - (MagnifierTip.ActualHeight); ;
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
//DV: Holding usually only works with touch https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.uielement.holding.aspx?f=255&MSPPError=-2147217396
private void LayoutGrid_Holding(object sender, HoldingRoutedEventArgs e)
{
//
}
//DV: pointer pressed supports both mouse and touch but fires immeadiatley. You'll have to figure out a delay strategy or using holding for touch and right click for mouse
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
//DV: pointer released supports both mouse and touch.
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
}
Magnifier.XAML
<UserControl
x:Class="App78.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="230"
Width="170">
<Grid Height="230" Width="170">
<!-- DV: This is the global transform I added -->
<Grid.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="MagnifierTransformGrid" x:FieldModifier="public"/>
</TransformGroup>
</Grid.RenderTransform>
<Ellipse Opacity="1" Visibility="Visible" Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}" HorizontalAlignment="Center" VerticalAlignment="Top" IsHitTestVisible="False" Width="135" Height="128" StrokeThickness="3" Margin="0,17,0,0" />
<Ellipse x:Name="MagnifierEllipse" x:FieldModifier="public" Opacity="1" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" Width="150" Height="150" Stroke="White" StrokeThickness="3" Margin="11,8,0,0" >
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform x:FieldModifier="public"
x:Name="CenterTransform"/>
<TranslateTransform x:FieldModifier="public"
x:Name="PositionTransform"/>
<ScaleTransform x:FieldModifier="public"
x:Name="ZoomTransform"/>
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z"
Fill="#FFF4F4F5"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
Height="227"
Width="171" ></Path>
</Grid>
</UserControl>
For ease , the project can be downloaded from here. For better understanding , I would like you to see this video. This is what exactly I need to implement.
I got it working with some changes and trigonometry here.
MainPage.xaml
<Page
x:Class="MagnifierApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MagnifierApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier
x:Name="MagnifierTip"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Visibility="Collapsed" />
</Grid>
</Page>
MainPage.xaml.cs
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace MagnifierApp
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
private bool isFirst = true;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
if (!e.Pointer.IsInContact)
{
return;
}
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX;
double offsetY;
double imageScale;
CalculateImageScaleAndOffsets(bi, out offsetX, out offsetY, out imageScale);
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.MagnifierTranslateTransform.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTranslateTransform.Y = this.pointerY - (MagnifierTip.ActualHeight);
bool tooHigh = MagnifierTip.MagnifierTranslateTransform.Y < 0;
bool tooLeft = MagnifierTip.MagnifierTranslateTransform.X < 0;
bool tooRight = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth;
if (tooHigh || tooLeft || tooRight)
{
double angle = 0.0;
if (tooLeft && !tooHigh)
{
var dx = -MagnifierTip.MagnifierTranslateTransform.X;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = Math.Asin(dx / r);
angle = arad * 180 / Math.PI;
}
else if (tooLeft && tooHigh)
{
var dx = -MagnifierTip.MagnifierTranslateTransform.X;
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad1 = Math.Asin(dx / r);
var arad2 = Math.Acos((r - dy) / r);
var arad = Math.Max(arad1, arad2);
angle = arad * 180 / Math.PI;
}
else if (tooHigh && !tooRight)
{
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = Math.Acos((r - dy) / r);
if (MagnifierTip.MagnifierTranslateTransform.X + Math.Sin(arad) * r + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth)
{
arad = -arad;
}
angle = arad * 180 / Math.PI;
}
else if (tooHigh)
{
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad1 = -Math.Acos((r - dy) / r);
var arad2 = -Math.Asin(dx / r);
var arad = Math.Min(arad1, arad2);
angle = arad * 180 / Math.PI;
}
else //if (tooRight)
{
var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = -Math.Asin(dx / r);
angle = arad * 180 / Math.PI;
}
MagnifierTip.RotateTransform.Angle = angle;
MagnifierTip.LensRotateTransform.Angle = -angle;
}
else
{
MagnifierTip.RotateTransform.Angle = 0;
MagnifierTip.LensRotateTransform.Angle = 0;
}
}
private void CalculateImageScaleAndOffsets(BitmapImage bi, out double offsetX, out double offsetY, out double imageScale)
{
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
imageScale = 1;
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
}
}
}
Magnifier.xaml
<UserControl
x:Class="MagnifierApp.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MagnifierApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="227"
Width="170">
<Grid
Height="227"
Width="170"
RenderTransformOrigin="0.5,1">
<Grid.RenderTransform>
<TransformGroup>
<RotateTransform
x:FieldModifier="public"
x:Name="RotateTransform"
Angle="0" />
<TranslateTransform
x:Name="MagnifierTranslateTransform"
x:FieldModifier="public" />
</TransformGroup>
</Grid.RenderTransform>
<Ellipse
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="152"
Height="152"
Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Margin="9,10,0,0"
RenderTransformOrigin="0.5,0.5"
StrokeThickness="0">
</Ellipse>
<Ellipse
x:Name="MagnifierEllipse"
x:FieldModifier="public"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="152"
Height="152"
Stroke="White"
Margin="9,10,0,0"
RenderTransformOrigin="0.5,0.5"
StrokeThickness="0">
<Ellipse.RenderTransform>
<RotateTransform
x:FieldModifier="public"
x:Name="LensRotateTransform"
Angle="0" />
</Ellipse.RenderTransform>
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform
x:FieldModifier="public"
x:Name="PositionTransform" />
<ScaleTransform
x:FieldModifier="public"
x:Name="ZoomTransform" />
<TranslateTransform
x:FieldModifier="public"
x:Name="CenterTransform" />
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path
Data="M85,11 C43.8548,11 10.5,44.3548 10.5,85.5 C10.5,126.645 43.8548,160 85,160 C126.145,160 159.5,126.645 159.5,85.5 C159.5,44.3548 126.145,11 85,11 z M85,0.5 C131.668,0.5 169.5,38.3319 169.5,85 C169.5,103.959 163.256,121.459 152.713,135.558 L151.895,136.625 L152,136.625 L84.2999,226 L18,136.625 L18.1054,136.625 L17.2872,135.558 C6.74375,121.459 0.5,103.959 0.5,85 C0.5,38.3319 38.3319,0.5 85,0.5 z"
Margin="0,0.5,0,0"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
StrokeThickness="0"
Fill="White" />
</Grid>
</UserControl>
The fix for 1 here is to Update the magnifier even when within
LayoutGrid_OnPointerPressed
So the updated method looks like
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
This gives you the desired behavior that you are talking about.
For resolving the bounds issue
calculate the pointer values above with respect to BitImage instead of LayoutGrid.
if (this.pointerX < 0.0 || this.pointerY < 0.0)
return;
if (this.pointerX > BigImage.ActualWidth || this.pointerY > BigImage.ActualHeight)
return;
var bi = (BitmapImage)BigImage.Source;
The rotation when reaching close the edge is all about carefully calculating the rotation angle as it reaches the edge and applying that Rotation transform.
MagnifierTip.RenderTransform = new RotateTransform{Angle=<CalculatedValue>, CenterX=<CalculatedValue>, CenterY=<CalculatedValue>};
Hope this helps.

Zooming and panning only work on first try using gesturelistener for Windows Phone

I’m new to windows phone development and trying to use the Microsoft.Phone.Controls.Toolkit for zooming and panning on an image. My issue is that zooming and panning only work on the first try. If I zoom/pan on the image, go elsewhere within the app and then go back to the image it doesn’t do anything. The OnPinchStarted method never runs when it is supposed to be envoked. I’ve used several different ways that I’ve found from searching here and elsewhere for the gesturelistener. Code is posted below. Before I go another route and toss out this way I wanted to see if there is something I’m missing with what I have because it works just fine the first time.
First try:
XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Image x:Name="Map"
Source="Images/park.png"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="Uniform" >
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener
PinchStarted="OnPinchStarted"
PinchDelta="OnPinchDelta"
DragDelta="OnDragDelta"/>
</toolkit:GestureService.GestureListener>
<Image.RenderTransform>
<CompositeTransform
ScaleX="1" ScaleY="1"
TranslateX="0" TranslateY="0"/>
</Image.RenderTransform>
</Image>
</Grid>
CS:
// these two fields fully define the zoom state:
private double TotalImageScale = 1d;
private Point ImagePosition = new Point(0, 0);
private const double MAX_IMAGE_ZOOM = 5;
private Point _oldFinger1;
private Point _oldFinger2;
private double _oldScaleFactor;
// Initializes the zooming operation
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
_oldFinger1 = e.GetPosition(Map, 0);
_oldFinger2 = e.GetPosition(Map, 1);
_oldScaleFactor = 1;
}
//Computes the scaling and translation to correctly zoom around your fingers.
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
var scaleFactor = e.DistanceRatio / _oldScaleFactor;
if (!IsScaleValid(scaleFactor))
return;
var currentFinger1 = e.GetPosition(Map, 0);
var currentFinger2 = e.GetPosition(Map, 1);
var translationDelta = GetTranslationDelta(
currentFinger1,
currentFinger2,
_oldFinger1,
_oldFinger2,
ImagePosition,
scaleFactor);
_oldFinger1 = currentFinger1;
_oldFinger2 = currentFinger2;
_oldScaleFactor = e.DistanceRatio;
UpdateImageScale(scaleFactor);
UpdateImagePosition(translationDelta);
}
//Moves the image around following your finger.
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
var translationDelta = new Point(e.HorizontalChange, e.VerticalChange);
if (IsDragValid(1, translationDelta))
UpdateImagePosition(translationDelta);
}
//Computes the translation needed to keep the image centered between your fingers.
private Point GetTranslationDelta(
Point currentFinger1, Point currentFinger2,
Point oldFinger1, Point oldFinger2,
Point currentPosition, double scaleFactor)
{
var newPos1 = new Point(
currentFinger1.X + (currentPosition.X - oldFinger1.X) * scaleFactor,
currentFinger1.Y + (currentPosition.Y - oldFinger1.Y) * scaleFactor);
var newPos2 = new Point(
currentFinger2.X + (currentPosition.X - oldFinger2.X) * scaleFactor,
currentFinger2.Y + (currentPosition.Y - oldFinger2.Y) * scaleFactor);
var newPos = new Point(
(newPos1.X + newPos2.X) / 2,
(newPos1.Y + newPos2.Y) / 2);
return new Point(
newPos.X - currentPosition.X,
newPos.Y - currentPosition.Y);
}
//Updates the scaling factor by multiplying the delta.
private void UpdateImageScale(double scaleFactor)
{
TotalImageScale *= scaleFactor;
ApplyScale();
}
//Applies the computed scale to the image control.
private void ApplyScale()
{
((CompositeTransform)Map.RenderTransform).ScaleX = TotalImageScale;
((CompositeTransform)Map.RenderTransform).ScaleY = TotalImageScale;
}
//Updates the image position by applying the delta.
//Checks that the image does not leave empty space around its edges.
private void UpdateImagePosition(Point delta)
{
var newPosition = new Point(ImagePosition.X + delta.X, ImagePosition.Y + delta.Y);
if (newPosition.X > 0) newPosition.X = 0;
if (newPosition.Y > 0) newPosition.Y = 0;
if ((Map.ActualWidth * TotalImageScale) + newPosition.X < Map.ActualWidth)
newPosition.X = Map.ActualWidth - (Map.ActualWidth * TotalImageScale);
if ((Map.ActualHeight * TotalImageScale) + newPosition.Y < Map.ActualHeight)
newPosition.Y = Map.ActualHeight - (Map.ActualHeight * TotalImageScale);
ImagePosition = newPosition;
ApplyPosition();
}
//Applies the computed position to the image control.
private void ApplyPosition()
{
((CompositeTransform)Map.RenderTransform).TranslateX = ImagePosition.X;
((CompositeTransform)Map.RenderTransform).TranslateY = ImagePosition.Y;
}
//Resets the zoom to its original scale and position
private void ResetImagePosition()
{
TotalImageScale = 1;
ImagePosition = new Point(0, 0);
ApplyScale();
ApplyPosition();
}
//Checks that dragging by the given amount won't result in empty space around the image
private bool IsDragValid(double scaleDelta, Point translateDelta)
{
if (ImagePosition.X + translateDelta.X > 0 || ImagePosition.Y + translateDelta.Y > 0)
return false;
if ((Map.ActualWidth * TotalImageScale * scaleDelta) + (ImagePosition.X + translateDelta.X) < Map.ActualWidth)
return false;
if ((Map.ActualHeight * TotalImageScale * scaleDelta) + (ImagePosition.Y + translateDelta.Y) < Map.ActualHeight)
return false;
return true;
}
//Tells if the scaling is inside the desired range
private bool IsScaleValid(double scaleDelta)
{
return (TotalImageScale * scaleDelta >= 1) && (TotalImageScale * scaleDelta <= MAX_IMAGE_ZOOM);
}
Second Try(from http://www.wintellect.com/blogs/jprosise/building-touch-interfaces-for-windows-phones-part-4)
XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid x:Name="ContentPanel" Grid.Row="1" RenderTransformOrigin="0.5,0.5" >
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener PinchDelta="OnPinchDelta"
PinchStarted="OnPinchStarted" DragDelta="OnDragDelta" />
</toolkit:GestureService.GestureListener>
<Grid.RenderTransform>
<CompositeTransform x:Name="HambyTransform" />
</Grid.RenderTransform>
<Image Source="Images/trails.png"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="Uniform" />
</Grid>
</Grid>
CS:
private double _cx, _cy;
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
_cx = HambyTransform.ScaleX;
_cy = HambyTransform.ScaleY;
}
//scale the map
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
//compute the new scaling factors
double cx = _cx * e.DistanceRatio;
double cy = _cy * e.DistanceRatio;
// If they're between 1.0 and 4.0, inclusive, apply them
if (cx >= 1.0 && cx <= 4.0 && cy >= 1.0 && cy <= 4.0)
{
HambyTransform.ScaleX = cx;
HambyTransform.ScaleY = cy;
}
}
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
HambyTransform.TranslateX += e.HorizontalChange;
HambyTransform.TranslateY += e.VerticalChange;
}
After many attempts to find a fix, which I never did, I started all over with a new project and the issue went away. Zoom/pan works fine. I don't know what happened or if something internal in my project was corrupt, but its working now. I hope this helps anybody who runs into the same issue. I had to do alot of copying/pasting code, but it worked.

Categories