Strange act of canvas when Scale/ Zoom in Silverlight - silverlight

I'm working on Zooming with Silverlight 5,
My Idea is to zoom according to mouse position on the Canvas, and the ability to Drag that Canvas,
The problem I have is when scale is less than 1, about 0.6 or 0.5, point to the corner of the canvas and wheel up, the canvas will change its position or "jump", any help please?
these two photos describe the status before Then after:
I have the following XAML:
<Grid x:Name="LayoutRoot" Background="White">
<ScrollViewer x:Name="sv" Margin="0,0,0,76" ScrollViewer.VerticalScrollBarVisibility="Disabled" Background="#FFE3E7F1">
<Canvas x:Name="grd" Height="394" Width="630">
<Canvas x:Name="cvs" Background="White" MouseWheel="cvs_MouseWheel" MouseLeftButtonDown="cvs_MouseLeftButtonDown" MouseLeftButtonUp="cvs_MouseLeftButtonUp" MouseMove="cvs_MouseMove" Height="391" Canvas.Left="2" Canvas.Top="1" Width="625">
<Canvas.Effect>
<DropShadowEffect ShadowDepth="0"/>
</Canvas.Effect>
<Rectangle Height="70" Canvas.Left="155" Canvas.Top="58" Width="79" Fill="#FFFFBFBF"/>
<Rectangle Height="70" Canvas.Left="544" Canvas.Top="126" Width="79" Fill="#FF8B92FF"/>
</Canvas>
</Canvas>
</ScrollViewer>
</Grid>
and here's the C#:
public partial class MainPage : UserControl
{
CompositeTransform canvasTransform = new CompositeTransform();
bool canDragCanvas;
double mouseRelatedPositionX = 0;
double mouseRelatedPositionY = 0;
public MainPage()
{
// Required to initialize variables
InitializeComponent();
}
private void cvs_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
var scaleFactor = 0.2*(e.Delta < 0?-1:1);
var centerX = e.GetPosition(cvs).X;
var centerY = e.GetPosition(cvs).Y;
if (centerX > cvs.ActualWidth * canvasTransform.ScaleX || centerX < 0 || centerY > cvs.ActualHeight * canvasTransform.ScaleY || centerY < 0)
{
centerX = cvs.ActualWidth/2;
centerY = cvs.ActualHeight/2;
}
canvasTransform.CenterX = centerX;
canvasTransform.CenterY = centerY;
canvasTransform.ScaleX += scaleFactor;
canvasTransform.ScaleY += scaleFactor;
cvs.RenderTransform = canvasTransform;
}
private void cvs_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
canDragCanvas = true;
mouseRelatedPositionX = e.GetPosition(cvs).X;
mouseRelatedPositionY = e.GetPosition(cvs).Y;
}
private void cvs_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
canDragCanvas = false;
}
private void cvs_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if(!canDragCanvas) return;
var leftValueToAdd = e.GetPosition(cvs).X - mouseRelatedPositionX;
var topValueToAdd = e.GetPosition(cvs).Y - mouseRelatedPositionY;
UpdateCanvasPosition(leftValueToAdd*canvasTransform.ScaleX, topValueToAdd*canvasTransform.ScaleX);
}
void UpdateCanvasPosition(double leftValueToAdd,double topValueToAdd)
{
var leftOffset = canvasTransform.CenterX - canvasTransform.CenterX * canvasTransform.ScaleX;
var rightOffset = (cvs.ActualWidth - canvasTransform.CenterX) - (cvs.ActualWidth - canvasTransform.CenterX) * canvasTransform.ScaleX;
var topOffset = canvasTransform.CenterY - canvasTransform.CenterY * canvasTransform.ScaleY;
var bottomOffset = (cvs.ActualHeight - canvasTransform.CenterY) - (cvs.ActualHeight - canvasTransform.CenterY) * canvasTransform.ScaleY;
var canvasLeftInBorders = Canvas.GetLeft(cvs)+ leftValueToAdd + leftOffset > 0;
var canvasRightInBorders = Canvas.GetLeft(cvs) + cvs.ActualWidth * canvasTransform.ScaleX + leftValueToAdd + leftOffset < grd.ActualWidth;
var canvasTopInBorders = Canvas.GetTop(cvs) + topValueToAdd + topOffset > 0;
var canvasBottomInBorders = Canvas.GetTop(cvs) + cvs.ActualHeight * canvasTransform.ScaleY + topValueToAdd + topOffset < grd.ActualHeight;
if (leftValueToAdd > 0)
{
if (canvasLeftInBorders)
leftValueToAdd = 0;
}
else if (leftValueToAdd < 0)
if (canvasRightInBorders)
leftValueToAdd = 0;
if (topValueToAdd > 0)
{
if (canvasTopInBorders)
topValueToAdd = 0;
}
else if (topValueToAdd < 0)
if (canvasBottomInBorders)
topValueToAdd = 0;
Canvas.SetLeft(cvs, Canvas.GetLeft(cvs) + leftValueToAdd);
Canvas.SetTop(cvs,Canvas.GetTop(cvs)+topValueToAdd);
}
}

Basically you are attaching mouse events to the surface that is zooming and the mouse coordinates get altered too. Silverlight is designed to still be interactive when you rotate, zoom and tilt.
You want to put a transparent layer over the top, that is not zoomed, and attached your mouse methods that that layer.
If you leave that layer turned on you will have to calculate collisions, but you can make it so that the layer only appears on mouse down and goes away on mouse up.

Related

How can I draw lines in WPF inkCanvas with sequence of PNGs

I was trying to make a method to draw some arrowheads from a PNG image with transparency.
While I moving mouse, the application will be plot that png along the path.
What is the way to make that?
Another form I was wondering is creating a polygnon shape. I was tryed draw the shape (triangle) when enter on mouse down event.
void MainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
double posX = e.GetPosition(null).X;
double posY = e.GetPosition(null).Y;
double posX1 = posX - (posX / 4);
double posY1 = posY - (posY / 4);
double posX2 = posX + (posX / 4);
double posY2 = posY;
double posX3 = posX - (posX / 4);
double posY3 = posY + (posY - posY / 3);
Polygon p = new Polygon();
p.Stroke = Brushes.Black;
p.Fill = Brushes.LightBlue;
p.StrokeThickness = 1;
p.HorizontalAlignment = HorizontalAlignment.Left;
p.VerticalAlignment = VerticalAlignment.Center;
p.Points = new PointCollection() { new Point(posX1, posY1), new Point(posX2, posY2), new Point(posX3, posY3), new Point(posX1, posY1) };
MyCanvas.Children.Add(p);
}
But still can't click and draw a triangle correctly in mouse (x,y) position.
http://i.stack.imgur.com/bM2i4.png
Here is a quick example.
XAML
<Canvas x:Name='DrawingCanvas'
Margin='30'
Background='LightBlue'
MouseDown='Canvas_MouseDown'
MouseMove='Canvas_MouseMove'
MouseUp='Canvas_MouseUp'>
<Rectangle>
<Rectangle.Fill>
<ImageBrush ImageSource='Arrow.png' x:Name='ArrowBrush'/>
</Rectangle.Fill>
</Rectangle>
</Canvas>
CODE
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private bool _isDrawing = false;
private Point _lastPosition;
const double MIN_MOVEMENT = 60;
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e) {
_isDrawing = true;
_lastPosition = e.GetPosition(DrawingCanvas);
// add the first arrow
AddArrow(_lastPosition);
}
private void Canvas_MouseMove(object sender, MouseEventArgs e) {
if (!_isDrawing)
{
return;
}
var currentPosition = e.GetPosition(DrawingCanvas);
// draw a new image if mouse has traveled minimum distance
if (Math.Abs((currentPosition.X - _lastPosition.X)) > MIN_MOVEMENT ||
Math.Abs((currentPosition.Y - _lastPosition.Y)) > MIN_MOVEMENT)
{
AddArrow(currentPosition);
_lastPosition = e.GetPosition(DrawingCanvas);
}
}
private void AddArrow(Point currentPosition) {
var rect = new Rectangle();
rect.Width = 120;
rect.Height = 120;
rect.Fill = ArrowBrush;
Canvas.SetTop(rect, currentPosition.Y);
Canvas.SetLeft(rect, currentPosition.X);
DrawingCanvas.Children.Add(rect);
}
private void Canvas_MouseUp(object sender, MouseButtonEventArgs e) {
_isDrawing = true;
}
}
Screenshot

Zooming To Mouse Point With ScrollView and ViewBox in Wpf

I have some paths drawn to the screen in wpf. The coordinates being used are quite small so they have been made to fill the screen with a view box. I am now trying to implement pan and zoom functionality. I would like to be able to zoom to wherever the mouse is in relation to the ui (i.e zoomed screen center is equal to mouse coordinates). The current outcome is that the center of the screen when zoomed is not reflective of the exact mouse position on the ui.
If you want to see what is happening... Here is my current solution file.
Or
Heres some code:
View Xaml
<Grid Name="MasterGrid" DataContext="{StaticResource mainWindowViewModel}">
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" Name="VisualisationScroller">
<Viewbox Name="VisualisationBox" Stretch="Fill" Loaded="VisualisationBox_Loaded">
<ItemsControl Name="CustomDrawingElement" ItemsSource="{Binding Trajectories}" Width="{Binding VisualisationWidth}" Height="{Binding VisualisationHeight}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="data:VisualisedTrajectory">
<Path Data = "{Binding PathData}" Stroke="Red" StrokeThickness="0.001" Fill="Transparent" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas
Background="DarkGray"
IsItemsHost="True">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1" />
<TranslateTransform />
</TransformGroup>
</ItemsControl.RenderTransform>
</ItemsControl>
</Viewbox>
</ScrollViewer>
</Grid>
View Model
public class MainWindowViewModel : BaseViewModel
{
public MainWindowViewModel()
{
VisualiseRawTrajectories();
}
private ObservableCollection<VisualisedTrajectory> _trajectories = new ObservableCollection<VisualisedTrajectory>();
public ObservableCollection<VisualisedTrajectory> Trajectories
{
get { return _trajectories; }
}
#region VisualisationDimensions
private double _visualisationWidth = 100;
public double VisualisationWidth
{
get { return _visualisationWidth; }
private set { _visualisationWidth = value; }
}
private double _visualisationHeight = 100;
public double VisualisationHeight
{
get { return _visualisationHeight; }
private set { _visualisationHeight = value; }
}
#endregion
public void VisualiseRawTrajectories()
{
var rand = new Random();
for (int i = 0; i < 5; i++)
{
var currentTrajectorySet = new List<Point>(); //each time through reinitialise
for (int j = 0; j < 5; j++)
{
currentTrajectorySet.Add(new Point(rand.NextDouble() * 0.5, rand.NextDouble() * 0.5)); //add a new point with max 0.5
if(j == 4)
{
currentTrajectorySet.Add(new Point(0.5, 0.5)); //for good measure :)
_trajectories.Add(new VisualisedTrajectory(CreatePathData(currentTrajectorySet)));
}
}
}
VisualisationHeight = 0.5;
VisualisationWidth = 0.5; //just for demonstration purposes
OnPropertyChanged("VisualisationHeight");
OnPropertyChanged("VisualisationWidth");
}
private Geometry CreatePathData(IList<Point> points)
{
var geometry = new StreamGeometry {FillRule = FillRule.EvenOdd};
using (StreamGeometryContext ctx = geometry.Open())
{
ctx.BeginFigure(points[0], false, false); //use the first index
ctx.PolyLineTo(points, true, true);
}
return (Geometry)geometry.GetAsFrozen();
}
}
View Code Behind
public MainWindow()
{
InitializeComponent();
VisualisationScroller.PreviewMouseWheel += OnPreviewMouseWheel;
}
private Point originalDimensions;
private void VisualisationBox_Loaded(object sender, RoutedEventArgs e)
{
Viewbox viewBox = sender as Viewbox;
viewBox.Width = viewBox.ActualWidth;
viewBox.Height = viewBox.ActualHeight;
originalDimensions = new Point(viewBox.ActualWidth, viewBox.ActualHeight);
}
#region Zoom
private int _numberDrawnItems = 0;
private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var zoomScale = new Point(CustomDrawingElement.RenderTransform.Value.M11,
CustomDrawingElement.RenderTransform.Value.M22); //gets the scale x and scale y
if (CustomDrawingElement != null && _numberDrawnItems != CustomDrawingElement.Items.Count) //if there was something draw to screen
{
_numberDrawnItems = CustomDrawingElement.Items.Count;
CustomDrawingElement.RenderTransformOrigin = new Point(0.5, 0.5); //if not set zoom from center
}
if (e.Delta > 0)
{
if (CustomDrawingElement != null)
{
VisualisationBox.Width = originalDimensions.X * (zoomScale.X + 1);
VisualisationBox.Height = originalDimensions.Y * (zoomScale.Y + 1);
var mousePosition = e.GetPosition(MasterGrid);
CustomDrawingElement.RenderTransformOrigin = new Point(mousePosition.X / MasterGrid.ActualWidth, mousePosition.Y / MasterGrid.ActualHeight);
CustomDrawingElement.RenderTransform = new MatrixTransform(zoomScale.X + 1, 0, 0, zoomScale.Y + 1, 0, 0);
}
}
if (e.Delta < 0)
{
if (zoomScale.X > 1 && zoomScale.Y > 1) //stops you from zooming out too much
{
if (CustomDrawingElement != null)
{
VisualisationBox.Width = VisualisationBox.Width - originalDimensions.X;
VisualisationBox.Height = VisualisationBox.Height - originalDimensions.Y;
CustomDrawingElement.RenderTransform = new MatrixTransform(zoomScale.X - 1, 0, 0, zoomScale.Y - 1, 0, 0);
}
}
}
e.Handled = true;
}
#endregion //Zooming code
}
Solved it changed the back code for zooming the View to:
if (e.Delta > 0)
{
if (CustomDrawingElement != null)
{
//zoom
_zoomScale++; //increase it now that we have zoomed
VisualisationBox.Width = _originalDimensions.X * (_zoomScale);
VisualisationBox.Height = _originalDimensions.Y * (_zoomScale);
ScrollerDimensions.Content = VisualisationScroller.ActualWidth + "x" + VisualisationScroller.ActualHeight;
BoxDimensions.Content = VisualisationBox.ActualWidth + "x" + VisualisationBox.ActualHeight;
var mousePosition = e.GetPosition(MasterGrid);
mousePosition = MasterGrid.TransformToVisual(VisualisationBox).Transform(mousePosition);
ScrolledPoint.Content = mousePosition.X + "," + mousePosition.Y;
VisualisationScroller.ScrollToHorizontalOffset(mousePosition.X);
VisualisationScroller.ScrollToVerticalOffset(mousePosition.Y);
}
}
if (e.Delta < 0)
{
if (_zoomScale > 1) //stops you from zooming out too much
{
if (CustomDrawingElement != null)
{
var mousePosition = e.GetPosition(MasterGrid);
_zoomScale -= 1; //decrease the zoom
VisualisationBox.Width = VisualisationBox.Width - _originalDimensions.X;
VisualisationBox.Height = VisualisationBox.Height - _originalDimensions.Y;
mousePosition = MasterGrid.TransformToVisual(VisualisationBox).Transform(mousePosition);
mousePosition = new Point(mousePosition.X - _originalDimensions.X, mousePosition.Y - _originalDimensions.Y);
VisualisationScroller.ScrollToHorizontalOffset(mousePosition.X);
VisualisationScroller.ScrollToVerticalOffset(mousePosition.Y);
}
}
}
e.Handled = true;
If anyone is interested HERE is the finished solution file with panning implemented too.

Make Object Follow Mouse On MouseDown and "Stick" On MouseUp

I'm working with a project that is WPF and VB.net. I want to visually simulate "dragging" an object (though I do not want to use standard drag and drop for reason of purpose).
Basically, I have a label object that, on its MouseDown event, I want it to follow the mouse cursor inside a 640x480 solid-size grid (but not outside of it!). Mind you, this grid is centered inside a full-screen window. Again, the object should not follow the mouse outside of the grid (I'm guessing a "ClipToBounds = True" here)
Then, on the label's MouseUp event, I want it to either stay in its current position or return to its original position, as determined by the value of a boolean variable set by another object's MouseEnter property.
Note, if it would be easier to work with, I can change the grid to a canvas in a cinch. I'm guessing that would be desirable.
So, after that long-winded explanation, here is my question (two-fold):
How do I make the object (label) follow the mouse cursor inside the grid/canvas, but not outside of it? This needs to happen on the MouseDown event of the label.
How do I make the object "stick" in its current position? (From this, I can probably figure out how to make it return to its original position on my own. :D )
My upvote to whoever can help me accomplish this goal the most efficiently! Thank you all very much.
How about something like this :
XAML :
<Canvas x:Name="canv" ToolTip="tt one" Width="400" Height="400" Background="Blue">
<Rectangle x:Name="rec" Fill="Red" Height="50" Width="50" MouseDown="Rectangle_MouseDown" MouseMove="Rectangle_MouseMove" MouseUp="Rectangle_MouseUp" />
</Canvas>
CODE-BEHIND :
private bool isDragging;
private void Rectangle_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
rec.CaptureMouse();
isDragging = true;
}
private void Rectangle_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (isDragging)
{
Point canvPosToWindow = canv.TransformToAncestor(this).Transform(new Point(0, 0));
Rectangle r = sender as Rectangle;
var upperlimit = canvPosToWindow.Y + (r.Height / 2);
var lowerlimit = canvPosToWindow.Y + canv.ActualHeight - (r.Height / 2);
var leftlimit = canvPosToWindow.X + (r.Width / 2);
var rightlimit = canvPosToWindow.X + canv.ActualWidth - (r.Width / 2);
var absmouseXpos = e.GetPosition(this).X;
var absmouseYpos = e.GetPosition(this).Y;
if ((absmouseXpos > leftlimit && absmouseXpos < rightlimit)
&& (absmouseYpos > upperlimit && absmouseYpos < lowerlimit))
{
Canvas.SetLeft(r, e.GetPosition(canv).X - (r.Width / 2));
Canvas.SetTop(r, e.GetPosition(canv).Y - (r.Height / 2));
}
}
}
private void Rectangle_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
rec.ReleaseMouseCapture();
isDragging = false;
}
This code could be enhanced, but I think you got the idea ;)
Based on #Bruno's, this is my solution:
double maxX;
double maxY;
private void OnRectMouseDown(object sender, MouseButtonEventArgs e)
{
maxX = canv.ActualWidth - rect.Width;
maxY = canv.ActualHeight - rect.Height;
rect.CaptureMouse();
rect.MouseMove += OnRectMouseMove;
rect.MouseUp += OnRectMouseUp;
}
private void OnRectMouseMove(object sender, MouseEventArgs e)
{
var pos = e.GetPosition(canv);
var newX = pos.X - (rect.Width / 2);
var newY = pos.Y - (rect.Height / 2);
if (newX < 0) newX = 0;
if (newX > maxX) newX = maxX;
if (newY < 0) newY = 0;
if (newY > maxY) newY = maxY;
rect.SetValue(Canvas.LeftProperty, newX);
rect.SetValue(Canvas.TopProperty, newY);
xVal.Content = (newX / maxX).ToString("F3");
yVal.Content = (newY / maxY).ToString("F3");
}
private void OnRectMouseUp(object sender, MouseButtonEventArgs e)
{
rect.ReleaseMouseCapture();
rect.MouseMove -= OnRectMouseMove;
rect.MouseUp -= OnRectMouseUp;
}
try this:
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
(name).Location = new Point(e.X,e.Y);
}
so that will make it so if you click the object will appear there

How do I only allow dragging in a circular path?

Is it possible to restrict the drag source to only move within the boundaries of a circular path when dragging it?
You don't need the 360-point path. Instead, as you are dragging, compute the current angle using Math.Atan2(Y,X), and then generate the point on the circle. You would still need to compute center and radius on resize and store them, or compute them inside MouseMove.
private void UserControl_MouseMove(object sender, MouseEventArgs e)
{
if (!isDraggingMarker)
return;
var position = e.GetPosition(this);
double angle = Math.Atan2(position.Y - center.Y, position.X - center.X);
var closest = new Point(center.X + radius*Math.Cos(angle),
center.Y + radius*Math.Sin(angle));
SetMarkerPosition(closest);
}
Create a circle of points and then when the mouse moves (and we are dragging) calculate the nearest point and snap to that point.
CircularDrag.xaml
<UserControl x:Class="DraggingBoundaries.CircularDrag"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
SizeChanged="UserControl_SizeChanged"
MouseMove="UserControl_MouseMove"
MouseLeave="UserControl_MouseLeave"
MouseLeftButtonUp="UserControl_MouseLeftButtonUp"
>
<Grid Background="White">
<Border
x:Name="Marker"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="14"
Height="14"
Background="CornflowerBlue"
CornerRadius="2"
BorderThickness="1"
BorderBrush="DarkGray"
MouseLeftButtonDown="Marker_MouseLeftButtonDown"
/>
</Grid>
</UserControl>
CircularDrag.xaml.cs
using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace DraggingBoundaries
{
public partial class CircularDrag : UserControl
{
List<Point> allowedWheelMarkerPositions;
bool isDraggingMarker;
public CircularDrag()
{
InitializeComponent();
}
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
var center = new Point(e.NewSize.Width / 2, e.NewSize.Height / 2);
var radius = (center.X < center.Y ? center.X : center.Y) - 15;
allowedWheelMarkerPositions = CreateCirclePath(center, radius);
SetMarkerPosition(allowedWheelMarkerPositions.First());
}
private List<Point> CreateCirclePath(Point center, double radius)
{
var result = new List<Point>();
for (double angle = 0; angle <= 360; angle++)
{
double angleR = angle * (Math.PI / 180);
double x = center.X + Math.Cos(angleR) * radius;
double y = center.Y - Math.Sin(angleR) * radius;
result.Add(new Point(x, y));
}
return result;
}
private void UserControl_MouseMove(object sender, MouseEventArgs e)
{
if (!isDraggingMarker)
return;
var position = e.GetPosition(this);
var closest = allowedWheelMarkerPositions
.OrderBy(p => GetDistance(position, p))
.First();
SetMarkerPosition(closest);
}
private void SetMarkerPosition(Point closest)
{
Marker.Margin = new Thickness(closest.X - Marker.Width / 2, closest.Y - Marker.Height / 2, 0, 0);
}
private double GetDistance(Point a, Point b)
{
var deltaX = a.X - b.X;
var deltaY = a.Y - b.Y;
return Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
}
private void Marker_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isDraggingMarker = true;
}
private void UserControl_MouseLeave(object sender, MouseEventArgs e)
{
isDraggingMarker = false;
}
private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDraggingMarker = false;
}
}
}

Silverlight cannot update property after animation runs

Im trying to create a simple pan and zoom app using silverlight 4, but Im having trouble updating the TranslateTransform and ScaleTransform properties after I run an animation on them.
I have tried to set the FillBehaviour to Stop, with no success.
Here is the code that I have:
<Canvas x:Name="LayoutRoot" Background="White" Width="800" Height="600">
<Canvas x:Name="PanningCanvas" Height="600" Width="800">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="CanvasScale"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform x:Name="CanvasTransform"/>
</TransformGroup>
</Canvas.RenderTransform>
<Rectangle x:Name="MovingRect" Fill="#FF834040" Height="209" Canvas.Left="219" Stroke="#FF500505" Canvas.Top="220" Width="329" StrokeThickness="2"/>
</Canvas>
<Button Content="Animate" Width="107" Canvas.Left="38" Canvas.Top="46" ClickMode="Press" Click="Button_Click"/>
</Canvas>
And here is the C# behind it all (slightly abbreviated)
public partial class MainPage : UserControl
{
private ManipulationProcessor2D manipulationProcessor;
public MainPage()
{
InitializeComponent();
this.manipulationProcessor = new ManipulationProcessor2D(Manipulations2D.Translate | Manipulations2D.Scale);
this.manipulationProcessor.Delta += OnManipulationDelta;
Touch.FrameReported += OnCapturedTouchReported;
}
private void OnManipulationDelta(object sender, Manipulation2DDeltaEventArgs e)
{
float zoomVal = e.Delta.ScaleX;
Point pinchPoint = new Point(e.OriginX, e.OriginY);
float xTranslation = e.Delta.TranslationX;
float yTranslation = e.Delta.TranslationY;
if (zoomVal != 1.0)
{
Zoom(zoomVal, PanningCanvas.RenderTransform.Inverse.Transform(pinchPoint), pinchPoint, xTranslation, yTranslation, true);
}
else if (xTranslation != 0 || yTranslation != 0)
{
Translate(xTranslation, yTranslation);
}
}
public void Zoom(double zoom, Point pinchPosition, Point physicalPosition, float xTranslation, float yTranslation, bool isFinger)
{
if (isFinger)
{
CanvasScale.ScaleX = CanvasScale.ScaleX * zoom;
CanvasScale.ScaleY = CanvasScale.ScaleY * zoom;
CanvasTransform.X = -1 * (pinchPosition.X * CanvasScale.ScaleX - physicalPosition.X);
CanvasTransform.Y = -1 * (pinchPosition.Y * CanvasScale.ScaleY - physicalPosition.Y);
}
else
{
CanvasScale.ScaleX = CanvasScale.ScaleX + zoom;
CanvasScale.ScaleY = CanvasScale.ScaleY + zoom;
}
}
private void Translate(float xTranslation, float yTranslation)
{
CanvasTransform.X += xTranslation;
CanvasTransform.Y += yTranslation;
}
private void OnCapturedTouchReported(object sender, TouchFrameEventArgs e)
{
//..removed..//
// process manipulations
this.manipulationProcessor.ProcessManipulators(DateTime.UtcNow.Ticks,manipulators);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
AnimatePosition(new Point(400, 300), 1);
AnimateScale(1.2, 1);
}
private Storyboard translateStoryboard = new Storyboard();
private Storyboard scaleStoryboard = new Storyboard();
public void AnimatePosition(Point destinationPoint, double lengthOfAnimation)
{
Point offset = new Point(destinationPoint.X, destinationPoint.Y);
var translationAnimationX = new DoubleAnimation() { SpeedRatio = 1, Duration = new Duration(TimeSpan.FromSeconds(lengthOfAnimation)), To = offset.X };
translateStoryboard.Children.Add(translationAnimationX);
Storyboard.SetTargetProperty(translationAnimationX, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"));
var translationAnimationY = new DoubleAnimation() { SpeedRatio = 1, Duration = new Duration(TimeSpan.FromSeconds(lengthOfAnimation)), To = offset.Y };
translateStoryboard.Children.Add(translationAnimationY);
Storyboard.SetTargetProperty(translationAnimationY, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"));
Storyboard.SetTarget(translateStoryboard, PanningCanvas);
translateStoryboard.Begin();
}
public void AnimateScale( double scale, double lengthOfAnimation)
{
var scaleAnimationX = new DoubleAnimation() { SpeedRatio = 1, Duration = new Duration(TimeSpan.FromSeconds(lengthOfAnimation)), To = scale };
scaleStoryboard.Children.Add(scaleAnimationX);
Storyboard.SetTargetProperty(scaleAnimationX, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"));
var scaleAnimationY = new DoubleAnimation() { SpeedRatio = 1, Duration = new Duration(TimeSpan.FromSeconds(lengthOfAnimation)), To = scale };
scaleStoryboard.Children.Add(scaleAnimationY);
Storyboard.SetTargetProperty(scaleAnimationY, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"));
Storyboard.SetTarget(scaleStoryboard, PanningCanvas);
scaleStoryboard.Begin();
}
}
The issue is that after I click the button, I cannot translate or zoom anymore...
please help!
Mark
Ok, so I got it working, but I dont know why.
Changing the animation to work like this:
Storyboard translateStoryboard = new Storyboard();
Point offset = new Point(destinationPoint.X, destinationPoint.Y);
var translationAnimationX = new DoubleAnimation() { SpeedRatio = 1, Duration = new Duration(TimeSpan.FromSeconds(lengthOfAnimation)), To = offset.X };
var translationAnimationY = new DoubleAnimation() { SpeedRatio = 1, Duration = new Duration(TimeSpan.FromSeconds(lengthOfAnimation)), To = offset.Y };
Storyboard.SetTargetProperty(translationAnimationX, new PropertyPath("X"));
Storyboard.SetTargetProperty(translationAnimationY, new PropertyPath("Y"));
Storyboard.SetTargetName(translationAnimationX, "CanvasTransform");
Storyboard.SetTargetName(translationAnimationY, "CanvasTransform");
translateStoryboard.Children.Add(translationAnimationX);
translateStoryboard.Children.Add(translationAnimationY);
translateStoryboard.Begin();
Did the trick, but I dont know why...
Can someone please explain it to me?

Resources