Im trying to rotate a <Image> of an arrow (placed in the middle of a 40x40 image). By what I remember from graphics class i need to first translate the image back to its center, rotate and then translate back:
TranslateTransform tTrans = new TranslateTransform();
tTrans.X -= 20;
tTrans.X -= 20;
RotateTransform rTrans = new RotateTransform();
rTrans.Angle = 60;
TranslateTransform t2Trans = new TranslateTransform();
tTrans.X += 20;
tTrans.X += 20;
imgWind.RenderTransform = ?;
Anyone got a good idea on how i can apply the transformations?
You can simply use
imgWind.RenderTransform = new RotateTransform(){ CenterX = 0.5, CenterY = 0.5, Angle = 45 };
Or in XAML:
<UIElement RenderTransformOrigin="0.5,0.5">
<UIElement.RenderTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="45" />
</UIElement.RenderTransform>
</UIElement>
By setting the CenterX and CenterY you don't have to translate before and after. In WPF (or silverlight for that matter) the transforms will take care of that themselfs.
Related
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);
}
Does anyone know of an easy way to animate a movement from an Image's current location to a new location (X,Y) using WPF animation with no XAML, 100% programmatically? And with no reference to "this" (with RegisterName etc).
I am trying to make an extension class for Image to do animation stuff on it. It is easy enough to change the width and height properties through animation, but after searching for location animation of an object it suddenly becomes more advanced.
As it is an extension class I will only have a reference to the actual Image object and the X and Y I want to move it to.
public static void MoveTo(this Image targetControl, double X, double Y, double Width, double Height){
//code here
...
}
Update:
Thanks. Almost working. It seems The GetTop and GetLeft returns 'NaN' not explicitly set. Found the workaround in this post: Canvas.GetTop() returning NaN
public static void MoveTo(this Image target, double newX, double newY) {
Vector offset = VisualTreeHelper.GetOffset(target);
var top = offset.Y;
var left = offset.X;
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, newY - top, TimeSpan.FromSeconds(10));
DoubleAnimation anim2 = new DoubleAnimation(0, newX - left, TimeSpan.FromSeconds(10));
trans.BeginAnimation(TranslateTransform.YProperty, anim1);
trans.BeginAnimation(TranslateTransform.XProperty, anim2);
}
I had to swap two of the values (FROM) with 0. I assume that must be because in this context the upper left corner of the picture is the origin? But now it works.
Try this:
public static void MoveTo(this Image target, double newX, double newY)
{
var top = Canvas.GetTop(target);
var left = Canvas.GetLeft(target);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(top, newY - top, TimeSpan.FromSeconds(10));
DoubleAnimation anim2 = new DoubleAnimation(left, newX - left, TimeSpan.FromSeconds(10));
trans.BeginAnimation(TranslateTransform.XProperty,anim1);
trans.BeginAnimation(TranslateTransform.YProperty,anim2);
}
Here it is... It changes the size and moves a MediaElement under the Canvas. Just input your parameters:
Storyboard story = new Storyboard();
DoubleAnimation dbWidth = new DoubleAnimation();
dbWidth.From = mediaElement1.Width;
dbWidth.To = 600;
dbWidth.Duration = new Duration(TimeSpan.FromSeconds(.25));
DoubleAnimation dbHeight = new DoubleAnimation();
dbHeight.From = mediaElement1.Height;
dbHeight.To = 400;
dbHeight.Duration = dbWidth.Duration;
story.Children.Add(dbWidth);
Storyboard.SetTargetName(dbWidth, mediaElement1.Name);
Storyboard.SetTargetProperty(dbWidth, new PropertyPath(MediaElement.WidthProperty));
story.Children.Add(dbHeight);
Storyboard.SetTargetName(dbHeight, mediaElement1.Name);
Storyboard.SetTargetProperty(dbHeight, new PropertyPath(MediaElement.HeightProperty));
DoubleAnimation dbCanvasX = new DoubleAnimation();
dbCanvasX.From = 0;
dbCanvasX.To = 5;
dbCanvasX.Duration = new Duration(TimeSpan.FromSeconds(.25));
DoubleAnimation dbCanvasY = new DoubleAnimation();
dbCanvasY.From = 0;
dbCanvasY.To = 5;
dbCanvasY.Duration = dbCanvasX.Duration;
story.Children.Add(dbCanvasX);
Storyboard.SetTargetName(dbCanvasX, mediaElement1.Name);
Storyboard.SetTargetProperty(dbCanvasX, new PropertyPath(Canvas.LeftProperty));
story.Children.Add(dbCanvasY);
Storyboard.SetTargetName(dbCanvasY, mediaElement1.Name);
Storyboard.SetTargetProperty(dbCanvasY, new PropertyPath(Canvas.TopProperty));
story.Begin(this);
<Viewbox Stretch="Uniform" StretchDirection="Both" SnapsToDevicePixels="True">
<Grid Width="640" Height="480" Name="MainLayout" SnapsToDevicePixels="True" Background="Black">
<Canvas Width="640" Height="480" Name="MainCanvas" SnapsToDevicePixels="True">
<MediaElement Height="171" HorizontalAlignment="Left" Name="mediaElement1" VerticalAlignment="Top" Width="337" LoadedBehavior="Manual" Margin="166,140,0,0" Canvas.Left="-162" Canvas.Top="-140" />
<Button Canvas.Left="294" Canvas.Top="196" Content="Button" Height="23" Name="button1" Width="75" Click="button1_Click" />
</Canvas>
</Grid>
</Viewbox>
UPDATE:
Instead of MediaElement use this line:
<Rectangle Height="171" HorizontalAlignment="Left" Name="mediaElement1" VerticalAlignment="Top" Width="337" Margin="166,140,0,0" Canvas.Left="-162" Canvas.Top="-140" Fill="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" />
And don't forget to put the C# code to:
private void button1_Click(object sender, RoutedEventArgs e) {}
You can use MediaElement as well but you have to define a VideoClip to see something ;)
Please find a solution that uses the Left and Top properties of Canvas for the extension method. See the following code:
public static void MoveTo(this Image target, Point newP)
{
Point oldP = new Point();
oldP.X = Canvas.GetLeft(target);
oldP.Y = Canvas.GetTop(target);
DoubleAnimation anim1 = new DoubleAnimation(oldP.X, newP.X, TimeSpan.FromSeconds(0.2));
DoubleAnimation anim2 = new DoubleAnimation(oldP.Y, newP.Y , TimeSpan.FromSeconds(0.2));
target.BeginAnimation(Canvas.LeftProperty , anim1);
target.BeginAnimation(Canvas.TopProperty, anim2);
}
This code is based on #DeanChalk's answer.
It moves an Image contained within a Canvas (RFID_Token) diagonally from the top-right to the bottom-left, positioned centrally over another Image within a Canvas (RFID_Reader).
<Canvas>
<Canvas x:Name="RFID_Reader_Canvas">
<Image x:Name="RFID_Reader" Source="RFID-Reader.png" Height="456" Width="682" Canvas.Left="37" Canvas.Top="524"/>
</Canvas>
<Canvas x:Name="RFID_Token_Canvas">
<Image x:Name="RFID_Token" Source="RFID-Token.png" Height="268" Width="343" Canvas.Left="874" Canvas.Top="70"/>
</Canvas>
</Canvas>
var StartX = Canvas.GetLeft(RFID_Token);
var StartY = Canvas.GetTop(RFID_Token);
var EndX = RFID_Reader.Width / 2 + Canvas.GetLeft(RFID_Reader) - StartX - (RFID_Token.Width / 2);
var EndY = RFID_Reader.Height / 2 + Canvas.GetTop(RFID_Reader) - StartY - (RFID_Token.Height / 2);
var AnimationX = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(1));
var AnimationY = new DoubleAnimation(0, EndY, TimeSpan.FromSeconds(1));
var Transform = new TranslateTransform();
RFID_Token_Canvas.RenderTransform = Transform;
Transform.BeginAnimation(TranslateTransform.XProperty, AnimationX);
Transform.BeginAnimation(TranslateTransform.YProperty, AnimationY);
I kept having NaN or 0 values for my nested elements, here's a modified version of Danny's answer :
public void MoveTo(Canvas canvas, FrameworkElement target, FrameworkElement destination)
{
Point oldPoint = target.TransformToAncestor(canvas).Transform(new Point(0, 0));
Point newPoint = destination.TransformToAncestor(canvas).Transform(new Point(0, 0));
var EndX = destination.Width / 2 + newPoint.X - oldPoint.X - (target.Width / 2);
var EndY = destination.Height / 2 + newPoint.Y - oldPoint.Y - (target.Height / 2);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(0.3));
DoubleAnimation anim2 = new DoubleAnimation(0, EndY, TimeSpan.FromSeconds(0.3));
trans.BeginAnimation(TranslateTransform.XProperty, anim1);
trans.BeginAnimation(TranslateTransform.YProperty, anim2);
}
I'm trying to implement a canvas zoom functionality in c# using matrix transform. I'm able to zoom in to one particular point, but while zooming out to the original scale(i've limited to original scale) the position of the canvas changes(out of the window) . I would like it to zoom out to its original position. Can anyone help?
Please find the code below:
<ScrollViewer Name="C1_S" Grid.Row="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" VerticalAlignment="Bottom" Grid.ColumnSpan="2" >
<Canvas Name="canvas_core0" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Bottom" Height="600" Width="1000000" MouseWheel="Canvas_MouseWheel" ClipToBounds="True" >
<Canvas.Background>
<DrawingBrush TileMode="Tile" Viewport="0,20,40,40" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,50,50"/>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="Gray" Thickness=".1"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Canvas.Background>
<Canvas.RenderTransform>
<MatrixTransform/>
</Canvas.RenderTransform>
</Canvas>
</ScrollViewer>
C# Code:`
private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
var element = sender as UIElement;
var position = e.GetPosition(element);
if(e.Delta>0)
{
previousposition = position;
}
var transform = element.RenderTransform as MatrixTransform;
var matrix = transform.Matrix;
scrollcountprevious = scrollcountcurrent;
scrollcountcurrent = scrollcountcurrent + e.Delta;
// var scale1 = scrollcountcurrent > scrollcountprevious ? 1.1 : scrollcountcurrent <scrollcountprevious0 ? 1.0 : (1.0 / 1.1); // choose appropriate scaling factor
var scale1=1.0;
if (scrollcountcurrent > scrollcountprevious)
{
scale1 = 1.1;
matrix.ScaleAtPrepend(scale1, scale1, position.X, position.Y);
transform.Matrix = matrix;
}
else if (scrollcountcurrent < scrollcountprevious&&scrollcountcurrent>=0)
{
scale1 = 1 / 1.1;
matrix.ScaleAtPrepend(scale1, scale1, previousposition.X, previousposition.Y);
transform.Matrix = matrix;
}
else
{
scale1 = 1;
scrollcountcurrent = 0;
matrix.ScaleAtPrepend(scale1, scale1, previousposition.X, previousposition.Y);
transform.Matrix = matrix;
}
}
Not sure if I understand what you're exactly trying to achieve. Also, having the Canvas in a ScrollViewer might mess things up.
But probably this MouseWheel handler does what you want:
private double scale = 1;
private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
var element = (UIElement)sender;
var position = e.GetPosition(element);
var matrix = Matrix.Identity;
scale = Math.Max(e.Delta > 0 ? scale * 1.1 : scale / 1.1, 1.0);
matrix.ScaleAt(scale, scale, position.X, position.Y);
((MatrixTransform)element.RenderTransform).Matrix = matrix;
}
In order to also scale the actual size of the Canvas in the ScrollViewer, scale its LayoutTransform instead of the RenderTransform:
<Canvas.LayoutTransform>
<MatrixTransform/>
</Canvas.LayoutTransform>
private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
var element = (FrameworkElement)sender;
var position = e.GetPosition(element);
var matrix = Matrix.Identity;
scale = Math.Max(e.Delta > 0 ? scale * 1.1 : scale / 1.1, 1.0);
matrix.ScaleAt(scale, scale, position.X, position.Y);
((MatrixTransform)element.LayoutTransform).Matrix = matrix;
}
I can rotate an image by:
RotateTransform aRotateTransform = new RotateTransform();
aRotateTransform.CenterX = 0.5;
aRotateTransform.CenterY = 0.5;
tateTransform.Angle = rotationAngle;
ImageBrush bgbrush = new ImageBrush();
bgbrush.RelativeTransform = aRotateTransform;
ScaleTransform s = new ScaleTransform();
s.ScaleX = -1; // how to set without overriding the rotation?
...
How can I scale it in addition? I tried using matrices without success.
You could use a TransformGroup like so:
TransformGroup tg = new Transformgroup();
tg.Children.Add(rotateTransform);
tg.Children.Add(scaleTransform);
bgbrush.RelativeTransform = tg;
You could use a CompositeTransform, it combines translation, rotation and scaling in a single matrix.
Just for completeness. Using Matrix transformations, you would get the expected result by this:
var transform = Matrix.Identity;
transform.RotateAt(rotationAngle, 0.5, 0.5);
transform.Scale(-1, 1);
bgbrush.RelativeTransform = new MatrixTransform(transform);
However, I guess that actually you want to keep the image centered, so you might use ScaleAt instead of Scale:
var transform = Matrix.Identity;
transform.RotateAt(rotationAngle, 0.5, 0.5);
transform.ScaleAt(-1, 1, 0.5, 0.5);
bgBrush.RelativeTransform = new MatrixTransform(transform);
If you just need to rotate the Brush it self and put it into a rectangle later without having to adjust anything else, this worked for me:
// Get the image from somewhere...
ib = new ImageBrush(bmp);
// Here I get it from a larger texture picture.
ib.Viewbox = this.GetVBRect(1728, 634);
ib.Transform = new RotateTransform()
{
CenterX = 0.5,
CenterY = 0.5,
Angle = 180,
};
ib.TileMode = TileMode.Tile;
I have a ContentPresenter and i try to scale the Content = ""(FrameworkElement)`.
Because if the Content.Size is to big it get cut off. I prepared these pictures to explain my problem.
We start from here. As you can see there is a part missing (yellow Panel is cuted).
So what I tried was ScaleTransform.
(XAML)
<ContentPresenter Content="SomeContent"
Grid.Row="1"
Grid.Column="0">
<ContentPresenter.RenderTransform>
<ScaleTransform ScaleX="0.75" ScaleY="0.75"></ScaleTransform>
</ContentPresenter.RenderTransform>
</ContentPresenter>
So after I added the ScaleTransform I got this result.
And obviously my Content got scaled, but its still cutted.
So I stopped to try it in XAML and this is what came out in c# .
public static FrameworkElement ScaleContent(FrameworkElement element, Size space)
{
ScaleTransform scaleTransform = new ScaleTransform();
double height = element.ActualHeight;
double width = element.ActualWidth;
if (height == 0 || width == 0)
{
element.Measure(new Size(space.Width,
space.Height));
element.Arrange(new Rect(0, 0, element.DesiredSize.Width, element.DesiredSize.Height));
height = element.ActualHeight;
width = element.ActualWidth;
}
double scaleWidth = space.Width / width;
double scaleHeight = space.width / height;
scaleTransform.ScaleX = scaleWidth;
scaleTransform.ScaleY = scaleHeight;
element.RenderTransform = scaleTransform;
return element;
}
And this is what came out.
To let you see the complete Example. I saved a Picture in a bigger Format, where the Content fits.
So what do you think is the problem ?
Thank you !