A while back after scouring the net and merging some code, I created PhotoViewer that is used in my cool camera app. It might not be perfect but it works acceptably. Here’s how it work in code.
Lets start with Xaml..
<Image x:Name="imgViewer" Stretch="UniformToFill" Width="728" Height="480">
<Image.RenderTransform>
<ScaleTransform x:Name="scaleTrans" ScaleX="1" ScaleY="1" />
</Image.RenderTransform>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Flick="GestureListener_Flick"
PinchStarted="GestureListener_PinchStart"
PinchDelta="GestureListener_PinchDelta"
PinchCompleted="GestureListener_PinchComplete"
DragStarted="GestureListener_DragStart"
DragDelta="GestureListener_DragDelta"
DragCompleted="GestureListener_DragCompleted"
Tap="GestureListener_Tap"
DoubleTap="GestureListener_DoubleTap"/>
</toolkit:GestureService.GestureListener>
</Image>
as you can see.. we start with Image and add a ScaleTransform. Next up with add GestureService from SL Toolkit. I am going to subscribe to some events. Here’s how the code behind looks
lets start by defining some data members
private readonly DispatcherTimer m_animationTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
private double m_friction = 0.99;
private Point m_scrollStartPoint;
private Point m_scrollTarget = new Point();
private double m_hvelocity;
private double m_vvelocity;
private double m_angle;
private double _cx, _cy;
Next up, set the Tick event in the constructor so we can do something on Tick of the DispatcherTimer.
m_animationTimer.Tick += new EventHandler(m_animationTimer_Tick);
Now the real code.. lets implement some of the event handlers
void m_animationTimer_Tick(object sender, EventArgs e)
{
if (m_hvelocity < 0.1)
m_hvelocity = 0;
if (m_vvelocity < 0.1)
m_vvelocity = 0;
if (m_hvelocity == 0 && m_vvelocity == 0)
{
m_animationTimer.Stop();
return;
}
if (m_angle > 0 && m_angle < 180)
scaleTrans.CenterY -= m_vvelocity;
else
scaleTrans.CenterY += m_vvelocity;
if(m_angle > 90 && m_angle < 270)
scaleTrans.CenterX += m_hvelocity;
else
scaleTrans.CenterX -= m_hvelocity;
m_hvelocity *= m_friction;
m_vvelocity *= m_friction;
if ((scaleTrans.CenterX <= 0 || scaleTrans.CenterX >= imgViewer.Width) || (scaleTrans.CenterY <= 0 || scaleTrans.CenterY >= imgViewer.Height))
{
if (scaleTrans.CenterX < 0)
scaleTrans.CenterX = 0;
if (scaleTrans.CenterX > imgViewer.Width)
scaleTrans.CenterX = imgViewer.Width;
if (scaleTrans.CenterY < 0)
scaleTrans.CenterY = 0;
if (scaleTrans.CenterY > imgViewer.Height)
scaleTrans.CenterY = imgViewer.Height;
m_animationTimer.Stop();
}
}
private void GestureListener_Flick(object sender, FlickGestureEventArgs e)
{
m_angle = e.Angle;
m_animationTimer.Stop();
m_scrollStartPoint = e.GetPosition(this);
m_scrollTarget.X = m_scrollStartPoint.X + e.HorizontalVelocity;
m_scrollTarget.Y = m_scrollStartPoint.Y + e.VerticalVelocity;
m_hvelocity = Math.Abs(e.HorizontalVelocity)/100;
m_vvelocity = Math.Abs(e.VerticalVelocity)/100;
if (m_scrollTarget.X < 0)
m_scrollTarget.X = 0;
if(m_scrollTarget.X > imgViewer.Width)
m_scrollTarget.X = imgViewer.Width;
if (m_scrollTarget.Y < 0)
m_scrollTarget.Y = 0;
if (m_scrollTarget.Y > imgViewer.Height)
m_scrollTarget.Y = imgViewer.Height;
m_animationTimer.Start();
}
private void GestureListener_PinchStart(object sender, PinchStartedGestureEventArgs e)
{
Point p1 = e.GetPosition(imgViewer, 0);
Point p2 = e.GetPosition(imgViewer, 1);
scaleTrans.CenterX = (p1.X + ((p2.X - p1.X) / 2));
scaleTrans.CenterY = (p1.Y + ((p2.Y - p1.Y) / 2));
_cx = scaleTrans.ScaleX;
_cy = scaleTrans.ScaleY;
}
private void GestureListener_PinchDelta(object sender, PinchGestureEventArgs e)
{
// Compute 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)
{
if ((cy - 1) < 0.1 && (cx - 1) < 0.1)
cx = cy = 1;
scaleTrans.ScaleX = cx;
scaleTrans.ScaleY = cy;
}
}
private void GestureListener_PinchComplete(object sender, PinchGestureEventArgs e)
{
}
private void GestureListener_DragStart(object sender, DragStartedGestureEventArgs e)
{
}
private void GestureListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
{
scaleTrans.CenterX = (scaleTrans.CenterX - e.HorizontalChange);
scaleTrans.CenterY = (scaleTrans.CenterY - e.VerticalChange);
if (scaleTrans.CenterX < 0)
scaleTrans.CenterX = 0;
else if (scaleTrans.CenterX > (imgViewer.Height * scaleTrans.ScaleX))
scaleTrans.CenterX = imgViewer.Height * scaleTrans.ScaleX;
if(scaleTrans.CenterY < 0)
scaleTrans.CenterY = 0;
else if (scaleTrans.CenterY > (imgViewer.Height * scaleTrans.ScaleY))
scaleTrans.CenterY = imgViewer.Height * scaleTrans.ScaleY;
}
private void GestureListener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
{
scaleTrans.CenterX = (scaleTrans.CenterX - e.HorizontalChange);
scaleTrans.CenterY = (scaleTrans.CenterY - e.VerticalChange);
if (scaleTrans.CenterX < 0)
scaleTrans.CenterX = 0;
else if (scaleTrans.CenterX > imgViewer.Width)
scaleTrans.CenterX = imgViewer.Width;
if (scaleTrans.CenterY < 0)
scaleTrans.CenterY = 0;
else if (scaleTrans.CenterY > (imgViewer.Height))
scaleTrans.CenterY = imgViewer.Height;
}
private void GestureListener_DoubleTap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
{
scaleTrans.ScaleX = scaleTrans.ScaleY = 1;
}
private void GestureListener_Tap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
{
m_animationTimer.Stop();
m_hvelocity = 0;
m_vvelocity = 0;
}