In windows 8 application, I am dragging an image inside a canvas. I want to bound that image inside canvas. How can I do it?
Here is my code:
XAML
<Canvas x:Name="canvas1" Grid.Row="0" Grid.Column="0" Margin="0" Grid.ColumnSpan="2" Grid.RowSpan="2" Background="White" >
<Image x:Name="image1" Height="100" Width="100" Margin="0" HorizontalAlignment="Left" ManipulationDelta="Image_ManipulationDelta_1" ManipulationMode="All">
<Image.RenderTransform>
<CompositeTransform/>
</Image.RenderTransform>
</Image></Canvas>`
C#
private void Image_ManipulationDelta_1(object sender, ManipulationDeltaRoutedEventArgs e)
{
var imag = (CompositeTransform)image1.RenderTransform;
imag.TranslateX += e.Delta.Translation.X;
imag.TranslateY += e.Delta.Translation.Y;}
You already have the code:
CompositeTransform imgTrans = image1.TransformToVisual(canvas1);
Windows.Foundation.Point imgPoint = imgTrans.TransformPoint(new Windows.Foundation.Point());
var imag = (CompositeTransform)image1.RenderTransform;
if (imgPoint.X <= 0)
{
imag .TranslateX -= imgPoint.X + 5;
imag .TranslateY += e.Delta.Translation.Y;
}
Related
How to make animation stay within the canvas at bigger sizes when the user clicks around the edge of the canvas? Currently, if sizes are too big and if user clicks near the edge of the canvas, the ellipse will grow outside of the canvas to cover the buttons. I need the animation to stay within the canvas to make it look like a slice of pizza essentially.
Should look like this:
Size 50 where user clicks near top left of canvas
Currently looks like this:
Size 50 where user clicks near top left of canvas
Xaml:
<Window.Resources>
<Storyboard x:Key="anim">
<DoubleAnimation
Storyboard.TargetName="myCircle"
Storyboard.TargetProperty="RadiusX"
AutoReverse="True"/>
<DoubleAnimation
Storyboard.TargetName="myCircle"
Storyboard.TargetProperty="RadiusY"
AutoReverse="True"/>
<DoubleAnimation
Storyboard.TargetName="path"
Storyboard.TargetProperty="Opacity"
AutoReverse="True"/>
</Storyboard>
</Window.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="23"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" Margin="0,0,0,1">
<Menu DockPanel.Dock="Top" Height="23">
<MenuItem Header="Main" RenderTransformOrigin="-1.896,0.643" HorizontalAlignment="Left" Width="39" Height="23">
<MenuItem Header="Exit, Esc" Click="MenuItem_Click_Exit"/>
</MenuItem>
</Menu>
</DockPanel>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" Name="pane">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Name="pane2">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Label Content="Size" Grid.Row="0" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
<Label Content="Fill Color" Grid.Row="1" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
<Label Content="Stroke Thickness" Grid.Row="2" Grid.Column="0" Height="25" VerticalAlignment="Stretch"/>
<Label Content="Stroke Color" Grid.Row="3" Grid.Column="0" VerticalAlignment="Top" Height="25"/>
<Slider x:Name="Slider_Size" Grid.Row="0" Grid.Column="1" Height="20" Width="45"
Minimum="5" Maximum="50"
AutoToolTipPlacement="BottomRight"
TickFrequency="1"
IsSnapToTickEnabled="True"
PreviewMouseUp="Slider_Size_PreviewMouseUp"/>
<Label Name="tempSize" Content="{Binding Path=Value, ElementName=Slider_Size}" Margin="0,25,0,131" Grid.Row="3" Visibility="Hidden"/>
<ComboBox Name="ComboBox_FillColor" Grid.Row="1" Grid.Column="1" Height="20" Width="45" SelectionChanged="ComboBox_FillColor_Selected"/>
<TextBox Name="textBox" Grid.Row="2" Grid.Column="1" Height="20" Width="45" TextChanged="textBox_TextChanged"/>
<ComboBox Name="ComboBox_StrokeColor" Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Height="20" Width="45" SelectionChanged="ComboBox_StrokeColor_Selected"/>
</Grid>
<Border Name ="border" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderBrush="Black" Grid.Column="1" BorderThickness="2">
<Canvas Name="canvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseDown="Canvas_MouseDown">
<Path x:Name="path">
<Path.Data>
<EllipseGeometry x:Name="myCircle"/>
</Path.Data>
</Path>
<Canvas.Background>
<SolidColorBrush Color="White" Opacity="0"/>
</Canvas.Background>
</Canvas>
</Border>
</Grid>
</Grid>
C#:
public partial class MainWindow : Window
{
private int size;
private SolidColorBrush fillColor;
private SolidColorBrush strokeColor;
private List<SolidColorBrush> colors;
private int fillIndex;
private int strokeIndex;
private int strokeThickness = 1;
private int fillColorDefault;
private int strokeColorDefault;
private Point? _start = null;
public MainWindow()
{
InitializeComponent();
addColors();
textBox.Text = strokeThickness.ToString();
parse();
}
private void MenuItem_Click_Exit(object sender, RoutedEventArgs e) { Environment.Exit(1); }
private void Window_KeyUp_ESC(object sender, KeyEventArgs e)
{
if (Key.Escape == e.Key)
MenuItem_Click_Exit(sender, e);
}
private void addColors()
{
colors = typeof(Brushes).GetProperties().Select(p => p.GetValue(null, null) as SolidColorBrush).ToList();
int count = 0;
foreach (SolidColorBrush color in colors)
{
ComboBox_FillColor.Items.Add(new Rectangle() { Height = 12, Width = 17.5, Fill = color });
ComboBox_StrokeColor.Items.Add(new Rectangle() { Height = 12, Width = 17.5, Fill = color });
if (color.Color == Colors.Red)
{
fillIndex = count;
fillColor = colors[fillIndex];
ComboBox_FillColor.SelectedIndex = count;
fillColorDefault = count;
}
if (color.Color == Colors.Black)
{
strokeIndex = count;
strokeColor = colors[strokeIndex];
ComboBox_StrokeColor.SelectedIndex = count;
strokeColorDefault = count;
}
count++;
}
}
private void ComboBox_FillColor_Selected(object sender, RoutedEventArgs e) { fillIndex = ComboBox_FillColor.SelectedIndex; fillColor = colors[fillIndex]; }
private void ComboBox_StrokeColor_Selected(object sender, RoutedEventArgs e) { strokeIndex = ComboBox_StrokeColor.SelectedIndex; strokeColor = colors[strokeIndex]; }
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
path.Stroke = strokeColor;
path.StrokeThickness = strokeThickness;
path.Fill = fillColor;
path.HorizontalAlignment = HorizontalAlignment.Stretch;
path.VerticalAlignment = VerticalAlignment.Stretch;
path.Stretch = Stretch.None;
path.SetValue(Grid.ColumnProperty, 1);
_start = Mouse.GetPosition((UIElement)sender);
myCircle.Center = (Point)_start;
var sb = FindResource("anim") as Storyboard;
var x = sb.Children.First() as DoubleAnimation;
x.To = 2 * size;
x.Duration = new Duration(TimeSpan.FromSeconds(0.5));
var y = sb.Children.ElementAt(1) as DoubleAnimation;
y.To = 2 * size;
y.Duration = new Duration(TimeSpan.FromSeconds(0.5));
var z = sb.Children.Last() as DoubleAnimation;
z.From = 0.0;
z.To = 1.0;
z.Duration = new Duration(TimeSpan.FromSeconds(0.5));
sb.Begin(path);
}
private void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
//regex where any string of chars besides numbers
Regex pattern = new Regex(#"^([^0-9]*)$", RegexOptions.Compiled);
Match result = pattern.Match(textBox.Text);
if (textBox.Text.ToString() == string.Empty)
return;
else if (result.Success)
{
MessageBox.Show("Invalid character entered. Integer numbers only. Stroke Thickness will be reseted to a default of 1.");
strokeThickness = 1;
textBox.Text = strokeThickness.ToString();
textBox.SelectAll();
}
else
{
int x;
if (int.TryParse(textBox.Text, out x))
strokeThickness = int.Parse(textBox.Text);
}
}
private void Slider_Size_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
parse();
}
private void parse()
{
int x;
if (int.TryParse(tempSize.Content.ToString(), out x))
size = x;
}
}
}
So you don't need the ellipse to stay within the Canvas, but you want to clip away the parts leaving it, right? Just set ClipToBounds (of Canvas) to true (can be done in Xaml).
I'm implementing a drag and drop wpf application and i have created 3 ellipse, i'm using thumb control to drag and drop the ellipse in the map, and i wish to get the drop position for ellipse. However i get an error when drag ellipse as below:
Unable to cast object of type 'System.Windows.Controls.Grid' to type
'System.Windows.Shapes.Ellipse'
My XAML:
<Window x:Class="DragandDropMFP.MainWindow"
xmlns:local="clr-namespace:DragandDropMFP"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="maingrid" MouseMove="MainGrid_MouseMove">
<Grid.Background>
<ImageBrush ImageSource="/Map/Capture.Png" />
</Grid.Background>
<Canvas>
<Thumb Canvas.Left="38" Canvas.Top="22" DragDelta="Thumb_DragDelta">
<Thumb.Template>
<ControlTemplate>
<Viewbox Width="50" Height="50">
<Grid Width="20" Height="20">
<Ellipse
Fill="Blue"
MouseMove="DragMouseMove"/>
<TextBlock HorizontalAlignment="Center" Text="Printer1" FontSize="4" TextAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Viewbox>
</ControlTemplate>
</Thumb.Template>
</Thumb>
<Thumb Canvas.Left="37" Canvas.Top="100" DragDelta="Thumb_DragDelta">
<Thumb.Template>
<ControlTemplate>
<Viewbox Width="50" Height="50">
<Grid Width="20" Height="20">
<Ellipse Fill="Yellow"
MouseMove="DragMouseMove"/>
<TextBlock HorizontalAlignment="Center" Text="Printer2" FontSize="4" TextAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Viewbox>
</ControlTemplate>
</Thumb.Template>
</Thumb>
<Thumb Canvas.Left="37" Canvas.Top="174" DragDelta="Thumb_DragDelta">
<Thumb.Template>
<ControlTemplate>
<Viewbox Width="50" Height="50">
<Grid Width="20" Height="20">
<Ellipse Fill="Red"
MouseMove="DragMouseMove"/>
<TextBlock HorizontalAlignment="Center" Text="Printer3" FontSize="4" TextAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Viewbox>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Canvas>
<TextBlock x:Name="ctlStatus" HorizontalAlignment="Stretch" Height="30" TextWrapping="Wrap" Text="status" VerticalAlignment="Bottom" RenderTransformOrigin="0.495,-4.7" />
</Grid>
</Window>
My xaml.cs :
public MainWindow()
{
InitializeComponent();
}
private void MainGrid_MouseMove(object sender, MouseEventArgs e)
{
Mouse.OverrideCursor = Cursors.Arrow;
objmoveposition(sender, e);
}
private void DragMouseMove(object sender, MouseEventArgs e)
{
Mouse.OverrideCursor = Cursors.Hand;
objmoveposition(sender, e);
}
private void objmoveposition(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (Mouse.OverrideCursor == Cursors.Hand)
{
Ellipse objTextbox = (Ellipse)sender; <--Error
if (objTextbox != null)
{
//----< Move Control >----
Point mousePoint = e.GetPosition(this);
//< Vertical >
int posY = (int)mousePoint.Y;
int actHeight = (int)Application.Current.MainWindow.Height;
int margin_Bottom = actHeight - (posY + (int)objTextbox.Height + (int)SystemParameters.CaptionHeight + (int)SystemParameters.BorderWidth + 4);
//< Horizontal >
int posX = (int)mousePoint.X;
int actWidth = (int)Application.Current.MainWindow.Width;
int margin_Right = actWidth - (posX + (int)objTextbox.Width + (int)SystemParameters.BorderWidth);
ctlStatus.Text = "Top=" + posY + " margin_bottom=" + margin_Bottom + " WinHeigth=" + actHeight + Environment.NewLine + " Left=" + posX + " margin_Right=" + margin_Right + "WinWidth=" + actWidth;
//ctlStatus.Text = "position=" + objTextbox.ActualHeight;
}
}
}
}
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
UIElement thumb = e.Source as UIElement;
Canvas.SetLeft(thumb, Canvas.GetLeft(thumb) + e.HorizontalChange);
Canvas.SetTop(thumb, Canvas.GetTop(thumb) + e.VerticalChange);
}
Output:
Your guidance is highly appreciated!
Thanks!
The sender is the ellipse that raised the event, so you can replace this:
Ellipse objTextbox = ellipse1;
with this;
Ellipse objTextbox = (Ellipse)sender;
As long as you only assign the event to ellipses this is safe. If it gets assigned to other object types you'll need to check the type of sender before casting.
You don't need the name of the object.
Just found out by changing the Ellipse to Grid and everything works like cham..
Ellipse objTextbox = (Ellipse)sender;
change to
Grid objTextbox = (Grid)sender;
Thanks!
I have canvas. In canvas i have 3 grids. And I can't animate them. I want to make slideshow with grids.
Here my xamll code:
<Canvas x:Name="canvas1" Width="200" Height="300" Background="AliceBlue">
<Grid Canvas.Left="0" Canvas.Right="200" Background="Red" Name="grid1">
</Grid>
<Grid Canvas.Left="200" Canvas.Right="400" Background="Blue" Name="grid2">
</Grid>
<Grid Canvas.Left="400" Canvas.Right="600" Background="Black" Name="grid3">
</Grid>
<Button Panel.ZIndex="1" Width="50" Height="50" Content="NEXT" Click="Button_Click">
</Button>
</Canvas>
Here my c# code:
private void Button_Click(object sender, RoutedEventArgs e)
{
TranslateTransform trans = new TranslateTransform();
TranslateTransform trans2 = new TranslateTransform();
grid1.RenderTransform = trans;
grid2.RenderTransform = trans2;
Storyboard storyboard = new Storyboard();
DoubleAnimation da = new DoubleAnimation();
DoubleAnimation da1 = new DoubleAnimation();
da = new DoubleAnimation(200, 0, TimeSpan.FromSeconds(1));
da1 = new DoubleAnimation(0, -200, TimeSpan.FromSeconds(1));
Storyboard.SetTarget(da1, grid1);
Storyboard.SetTargetProperty(da1, new PropertyPath("RenderTransform.(TranslateTransform.X)"));
Storyboard.SetTarget(da, grid2);
Storyboard.SetTargetProperty(da, new PropertyPath("RenderTransform.(TranslateTransform.X)"));
storyboard.Children.Add(da1);
storyboard.Children.Add(da);
//this.BeginStoryboard(storyboard, HandoffBehavior.SnapshotAndReplace, true);
FrameworkElement sd = new FrameworkElement();
sd.BeginStoryboard(storyboard, HandoffBehavior.Compose, true);
}
For now, i want to animate only 2 grids. i press button, and nothing happened.
I need to animate from the code.
That's how I do it:
Fist the button outside of the Canvas and place the Canvas in a Grid that has a Clip with the size of the objects inside the Canvas, I set 200x200 as example:
<Grid Width="200" Height="200">
<Grid.Clip>
<RectangleGeometry Rect="0,0,200,200"/>
</Grid.Clip>
<Canvas x:Name="canvas1" Background="AliceBlue" >
<Canvas.RenderTransform>
<TranslateTransform/>
</Canvas.RenderTransform>
<Grid Canvas.Left="0" Canvas.Right="200" Width="200" Height="200" Background="Red" Name="grid1">
</Grid>
<Grid Canvas.Left="200" Canvas.Right="400" Width="200" Height="200" Background="Blue" Name="grid2">
</Grid>
<Grid Canvas.Left="400" Canvas.Right="600" Width="200" Height="200" Background="Black" Name="grid3">
</Grid>
<Grid Canvas.Left="400" Canvas.Right="600" Width="200" Height="200" Background="Orange" Name="grid4">
</Grid>
</Canvas>
<Button VerticalAlignment="Bottom" Width="50" Height="50" Content="NEXT" Click="Button_Click">
</Button>
</Grid>
And now the logic for the button event
private void Button_Click(object sender, RoutedEventArgs e)
{
var offsetX = (canvas1.RenderTransform as TranslateTransform).X;
var finalX = offsetX - 200;
Storyboard sb = new Storyboard();
var da = new DoubleAnimation(offsetX, finalX, TimeSpan.FromSeconds(1));
Storyboard.SetTarget(da, canvas1);
Storyboard.SetTargetProperty(da, new PropertyPath("RenderTransform.(TranslateTransform.X)"));
sb.Children.Add(da);
sb.Begin();
}
I think is easy to understand and just one storyboard.
I hope it is the solution for your case.
How can I restrict panning for canvas when mouse-pointer comes out of border.. My panning code is below:
private Point origin;
private Point start;
void Path_MouseMove(object sender, MouseEventArgs e)
{
if (rBtnPanning.IsChecked.Value)
{
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);
}
}
void path_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 path_MouseLeftBtnUp(object sender, MouseButtonEventArgs e)
{
((Path)sender).ReleaseMouseCapture();
}
Xaml code is below added:
As it has Canvas between Border. I need to restrict the panning, that it shouldn't move when I hold MouseLeftDown and drag out of border.
<Window x:Class="DummyTestWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1000" Width="1000" Background="Bisque">
<Grid Height="1000" Width="1000" x:Name="grid">
<Border x:Name="clipBorder" Height="810" Width="810" BorderThickness="2" BorderBrush="Black" ClipToBounds="True">
<Canvas x:Name="CanvasPanel" Height="800" Width="800" Background="Transparent" >
</Canvas>
</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>
Please any one can suggest me.
Regards,
Viswa.
Just add if condition before apply matrix transform in MouseMove Event in code behind code. The code is as follows:
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);
}
}
Regards,
Viswa
The x border for the image can be found with the following formula:
let width = image.width;
borderX = Math.ceil(((width * this.currentScale) - width) / (2 * this.currentScale) );
I am trying to restrict the image movement placed inside a canvas on touch manipulation while translating, rotating and scaling(pinch to zoom).
I tried to implement manipulationcontainer property but it didn't give me the desired results.
Any help is appreciated!!!
XAMl Code-
<Canvas x:Name="mainCanvas" Grid.Column="1">
<Canvas.Background>
<ImageBrush x:Name="imgBg" ImageSource="/Images/Backgrounds/bg0.jpg"/>
</Canvas.Background>
<Image x:Name="imgTest" Source="/Images/Sample.jpg" ManipulationStarted="image_OnManipulationStarted" ManipulationDelta="image_OnManipulationDelta" Height="150" Width="190" Canvas.Top="80" Margin="12,0,0,0" Stretch="Fill">
<Image.RenderTransform>
<CompositeTransform />
</Image.RenderTransform>
</Image>
<Image x:Name="imgTest2" Source="/Images/Sample2.jpg" ManipulationStarted="image_OnManipulationStarted" ManipulationDelta="image_OnManipulationDelta" Height="150" Width="190" Canvas.Top="80" Canvas.Left="200" Margin="12,0,0,0" Stretch="Fill">
<Image.RenderTransform>
<CompositeTransform />
</Image.RenderTransform>
</Image>
<Image x:Name="imgTest3" Source="/Images/Sample3.jpg" ManipulationStarted="image_OnManipulationStarted" ManipulationDelta="image_OnManipulationDelta" Height="150" Width="190" Canvas.Top="240" Margin="12,0,0,0" Stretch="Fill">
<Image.RenderTransform>
<CompositeTransform />
</Image.RenderTransform>
</Image>
<Image x:Name="imgTest4" Source="/Images/Sample4.jpg" ManipulationStarted="image_OnManipulationStarted" ManipulationDelta="image_OnManipulationDelta" Height="150" Width="190" Canvas.Top="240" Canvas.Left="200" Margin="12,0,0,0" Stretch="Fill">
<Image.RenderTransform>
<CompositeTransform />
</Image.RenderTransform>
</Image>
</Canvas>
C# Code-
double _scaleX, _scaleY, _translationX, _translationY;
private void image_OnManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
// the user has started manipulating the screen, set starting points
var transform = (CompositeTransform)((System.Windows.FrameworkElement)(sender)).RenderTransform;
_scaleX = transform.ScaleX;
_scaleY = transform.ScaleY;
_translationX = transform.TranslateX;
_translationY = transform.TranslateY;
e.ManipulationContainer = mainCanvas;
e.Handled = true;
}
private void image_OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var transform = (CompositeTransform)((FrameworkElement)(sender)).RenderTransform;
Image imgSender = sender as Image;
// pan
transform.TranslateX = _translationX + e.CumulativeManipulation.Translation.X;
transform.TranslateY = _translationY + e.CumulativeManipulation.Translation.Y;
if (e.PinchManipulation != null)
{
// zoom
transform.CenterX = e.PinchManipulation.Original.Center.X;
transform.CenterY = e.PinchManipulation.Original.Center.Y;
transform.ScaleX = _scaleX * e.PinchManipulation.CumulativeScale;
transform.ScaleY = _scaleY * e.PinchManipulation.CumulativeScale;
//rotate
transform.Rotation = angleBetween2Lines(e.PinchManipulation.Current, e.PinchManipulation.Original);
}
Point p = new Point(0, 0);
Rect containingRect = new Rect(p,((FrameworkElement)e.ManipulationContainer).RenderSize);
Rect shapeBounds = imgSender.RenderTransform.TransformBounds(new Rect(p,imgSender.RenderSize));
Point bound = new Point(shapeBounds.Top, shapeBounds.Bottom);
if (e.IsInertial && !containingRect.Contains(bound))
{
e.Complete();
}
e.Handled = true;
}
Keeping the parent canvas as a child of ScrollViewer worked for me.