This code allows for the phone to be used in either portrait or landscape mode. Aspect ratio is not locked, but the code could be tweaked to enforce this.
Try it out here in my WP8 Meme Maker app.
How it works
Two images sit within a grid, with the semi-transparent instance behind in z-order. A rectangle is positioned in each corner. The topmost image is clipped to a rectangle defined by dragging the rectangles around.
XAML:
<Grid> <Grid HorizontalAlignment="Center" VerticalAlignment="Center"> <Image Source="Assets/someImage.png" IsHitTestVisible="False" Opacity="0.3" ></Image> <Image Name="imgSauce" Source="Assets/someImage.png" > <Image.Clip> <RectangleGeometry x:Name="clipRect" ></RectangleGeometry> </Image.Clip> </Image> <Rectangle Name="rectTopLeft" Width="20" Height="20" Margin="-10" Fill="Yellow" HorizontalAlignment="Left" VerticalAlignment="Top" > <Rectangle.RenderTransform> <TranslateTransform></TranslateTransform> </Rectangle.RenderTransform> </Rectangle> <Rectangle Name="rectTopRight" Width="20" Height="20" Margin="-10" Fill="Yellow" HorizontalAlignment="Right" VerticalAlignment="Top" > <Rectangle.RenderTransform> <TranslateTransform></TranslateTransform> </Rectangle.RenderTransform> </Rectangle> <Rectangle Name="rectBotLeft" Width="20" Height="20" Margin="-10" Fill="Yellow" HorizontalAlignment="Left" VerticalAlignment="Bottom" > <Rectangle.RenderTransform> <TranslateTransform></TranslateTransform> </Rectangle.RenderTransform> </Rectangle> <Rectangle Name="rectBotRight" Width="20" Height="20" Margin="-10" Fill="Yellow" HorizontalAlignment="Right" VerticalAlignment="Bottom" > <Rectangle.RenderTransform> <TranslateTransform></TranslateTransform> </Rectangle.RenderTransform> </Rectangle> </Grid> </Grid>
Code Behind:
using System; using System.Windows; using Microsoft.Phone.Controls; using System.Windows.Media; using System.Windows.Shapes;
public partial class CropPage : Page { private Rectangle _draggedRect = null; public CropPage() { InitializeComponent(); var rects = new Rectangle[] { rectTopRight, rectTopLeft, rectBotRight, rectBotLeft }; Point _dragOrigin =new Point(); double origLeftPerc= 0, origRightPerc = 0, origTopPerc = 0, origBotPerc = 0; var setOrigin = new Action<Point>((p) => { _dragOrigin = p; origLeftPerc = this._clipLeftPerc; origRightPerc = this._clipRightPerc; origTopPerc = this._clipTopPerc; origBotPerc = this._clipBotPerc; }); foreach (var aRect in rects) { aRect.MouseLeftButtonDown += (s, e) => { var r = (Rectangle)s; _draggedRect = r; setOrigin( e.GetPosition(this.imgSauce)); r.CaptureMouse(); }; aRect.MouseLeftButtonUp += (s, e) => { _draggedRect = null; }; aRect.MouseMove += (s, e) => { if (_draggedRect != null) { var pos = e.GetPosition(this.imgSauce); if (s == this.rectTopLeft || s == this.rectTopRight) { // Adjust top _clipTopPerc = origTopPerc + (pos.Y - _dragOrigin.Y) / imgSauce.ActualHeight; } if (s == this.rectTopLeft || s == this.rectBotLeft) { // Adjust Left _clipLeftPerc = origLeftPerc + (pos.X - _dragOrigin.X) / imgSauce.ActualWidth; } if (s == this.rectBotLeft || s == this.rectBotRight) { // Adjust bottom _clipBotPerc = origBotPerc - (pos.Y - _dragOrigin.Y) / imgSauce.ActualHeight; } if (s == this.rectTopRight || s == this.rectBotRight) { // Adjust Right _clipRightPerc = origRightPerc - (pos.X - _dragOrigin.X) / imgSauce.ActualWidth; } this.updateClipAndTransforms(); } }; } var draggingImg = false; imgSauce.MouseLeftButtonDown += (s, e) => { setOrigin( e.GetPosition(this.imgSauce)); imgSauce.CaptureMouse(); draggingImg = true; }; imgSauce.MouseLeftButtonUp += (s, e) => { draggingImg = false; }; imgSauce.MouseMove += (s, e) => { if (draggingImg) { var pos = e.GetPosition(this.imgSauce); var xAdjust = (pos.X - _dragOrigin.X) / imgSauce.ActualWidth; var yAdjust = (pos.Y - _dragOrigin.Y) / imgSauce.ActualHeight; _clipLeftPerc = origLeftPerc + xAdjust; _clipRightPerc = origRightPerc - xAdjust; _clipTopPerc = origTopPerc + yAdjust; _clipBotPerc = origBotPerc - yAdjust; this.updateClipAndTransforms(); } }; imgSauce.SizeChanged += (x,y) => { this.updateClipAndTransforms(); }; this.updateClipAndTransforms(); } private double _clipLeftPerc, _clipRightPerc, _clipTopPerc, _clipBotPerc = 0; void updateClipAndTransforms() { // Check bounds if (_clipLeftPerc + _clipRightPerc >= 1) _clipLeftPerc = (1 - _clipRightPerc) - 0.04; if (_clipTopPerc + _clipBotPerc >= 1) _clipTopPerc = (1 - _clipBotPerc) - 0.04; if (_clipLeftPerc < 0) _clipLeftPerc = 0; if (_clipRightPerc < 0) _clipRightPerc = 0; if (_clipBotPerc < 0) _clipBotPerc = 0; if (_clipTopPerc < 0) _clipTopPerc = 0; if (_clipLeftPerc >= 1) _clipLeftPerc = 0.99; if (_clipRightPerc >= 1) _clipRightPerc = 0.99; if (_clipBotPerc >= 1) _clipBotPerc = 0.99; if (_clipTopPerc >= 1) _clipTopPerc = 0.99; // Image Clip var leftX = _clipLeftPerc * this.imgSauce.ActualWidth; var topY = _clipTopPerc * this.imgSauce.ActualHeight; clipRect.Rect = new Rect(leftX, topY, (1 -_clipRightPerc) * this.imgSauce.ActualWidth - leftX, (1 - _clipBotPerc) * this.imgSauce.ActualHeight - topY); // Rectangle Transforms ((TranslateTransform)this.rectTopLeft.RenderTransform).X = clipRect.Rect.X; ((TranslateTransform)this.rectTopLeft.RenderTransform).Y = clipRect.Rect.Y; ((TranslateTransform)this.rectTopRight.RenderTransform).X = -_clipRightPerc * this.imgSauce.ActualWidth; ((TranslateTransform)this.rectTopRight.RenderTransform).Y = clipRect.Rect.Y; ((TranslateTransform)this.rectBotLeft.RenderTransform).X = clipRect.Rect.X; ((TranslateTransform)this.rectBotLeft.RenderTransform).Y = - _clipBotPerc * this.imgSauce.ActualHeight; ((TranslateTransform)this.rectBotRight.RenderTransform).X = -_clipRightPerc * this.imgSauce.ActualWidth; ((TranslateTransform)this.rectBotRight.RenderTransform).Y = -_clipBotPerc * this.imgSauce.ActualHeight; } }