How to fix DoubleAnimation for rotation of an image? - c#

I am trying to rotate an image on an EventHandler, for an Event that i created. The Event Handler works perfectly fine. Yet the image does not rotate and I do not know what I am missing.
This UserControl is not displayed like this, but in a Grid of another UserControl.
<UserControl x:Class="Mabri.Module.P83.View.SchematicSystemView"
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"
xmlns:local="clr-namespace:Mabri.Module.P83.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Image x:Name="drehteller" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</UserControl>
This is the code behind the XAML, at first I tried having this in a separat class, if this is possible it would be even better, but at the moment I am just trying to get this running.
{
private System.Windows.Controls.Image drehteller_image;
private int currentAngle = 0;
public SchematicSystemView()
{
EventAggregator.SubscribeEvent(this);
InitializeComponent();
drehteller_image = new System.Windows.Controls.Image()
{
Stretch = Stretch.Uniform,
Source = new BitmapImage(new Uri("pack://application:,,,/Mabri.Module.P83;Component/Images/drehteller_unbestueckt.png")),
RenderTransform = new RotateTransform()
};
drehteller.Source = drehteller_image.Source;
}
public void OnEventHandler(DrehtellerMoved e)
{
EventAggregator.PublishEvent(new LogInfo
{
Title = "Should rotate",
Summary = "The drehteller should rotate now",
Detail = "The drehteller should rotate " + currentAngle + " is the current angle",
LogLevel = LogLevel.Information
});
int steps = e.steps;
double timeforonestep = e.speed / e.steps;
int angle = steps * 72;
int angle_to_reach = currentAngle + angle;
Storyboard storyboard = new Storyboard();
storyboard.Duration = new Duration(TimeSpan.FromSeconds(timeforonestep * steps));
DoubleAnimation rotateAnimation = new DoubleAnimation()
{
From = currentAngle,
To = currentAngle + angle,
Duration = storyboard.Duration
};
Storyboard.SetTarget(rotateAnimation, drehteller);
Storyboard.SetTargetProperty(rotateAnimation, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
storyboard.Children.Add(rotateAnimation);
storyboard.Begin();
currentAngle = angle_to_reach;
}
}
In the testing case e.steps equals 1 and e.speed equals 10.0.
I expected the image to turn for 72 degrees, but for some reasons nothing happens, except for the LogMessage at the beginning of the handler.

First, assign a RotateTransform to the RenderTransform property of the Image. Otherwise it can't be animated.
<Image x:Name="drehteller" HorizontalAlignment="Center" VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<RotateTransform/>
</Image.RenderTransform>
</Image>
Then instead of setting From and To just set By. And drop the Storyboard and start the animation directly on the control's RenderTransform
var animation = new DoubleAnimation
{
By = angle,
Duration = TimeSpan.FromSeconds(...)
};
drehteller.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, animation);
Finally, do not create the intermediate Image element. That is redundant.
drehteller.Source = new BitmapImage(new Uri("..."));

Related

How to move a picture to where a cursor clicked on the canvas

I have an image of a character and I want to be able to move it to the position of where my/the user's cursor is clicking on my canvas. for example-if the user clicked on the middle of the screen, I want his animal to walk there gradually, (at a certain speed). Does anyone know how I can achieve that?
How to move a picture to where a cursor clicked on the canvas
For your requirement, you could create DoubleAnimation for image. You could get your cursor clicked position with PointerPressed event then pass the position X,Y value to DoubleAnimation ToProperty. I created the sample you could refer.
Code behind
public MainPage()
{
this.InitializeComponent();
RootCanvasLayout.PointerPressed += new PointerEventHandler(Pointer_Pressed);
}
private void Pointer_Pressed(object sender, PointerRoutedEventArgs e)
{
Windows.UI.Input.PointerPoint currentPoint = e.GetCurrentPoint(RootCanvasLayout);
CreateAnimation(currentPoint);
}
void CreateAnimation(PointerPoint point)
{ var duration = new Duration(TimeSpan.FromMilliseconds(1000));
DoubleAnimation doubleAnimationX = new DoubleAnimation();
DoubleAnimation doubleAnimationY = new DoubleAnimation();
doubleAnimationX.To = point.Position.X-Pic.ActualWidth/2;
doubleAnimationX.Duration = duration;
doubleAnimationY.To = point.Position.Y-Pic.ActualHeight/2;
doubleAnimationY.Duration = duration;
var conStoryboard = new Storyboard();
conStoryboard.Children.Add(doubleAnimationX);
conStoryboard.Children.Add(doubleAnimationY);
Storyboard.SetTarget(doubleAnimationX, Pic);
Storyboard.SetTarget(doubleAnimationY, Pic);
Storyboard.SetTargetProperty(doubleAnimationX, "(Canvas.Left)");
Storyboard.SetTargetProperty(doubleAnimationY, "(Canvas.Top)");
conStoryboard.Begin();
}
Xaml
<Canvas Name="RootCanvasLayout" Background="Ivory">
<Image Name="Pic" Width="100" Height="100"
Source="Assets/hello.png"
Canvas.Left="0"
Canvas.Top = "0"/>
</Canvas>

WPF Manipulation Events are not getting triggered

I am studying and developing a touch screen application.
I tried below sample code:
Xaml:
<Window x:Class = "WPFMultiTouchInput.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local = "clr-namespace:WPFMultiTouchInput"
mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "604">
<Window.Resources>
<MatrixTransform x:Key = "InitialMatrixTransform">
<MatrixTransform.Matrix>
<Matrix OffsetX = "200" OffsetY = "200"/>
</MatrixTransform.Matrix>
</MatrixTransform>
</Window.Resources>
<Canvas>
<Rectangle Name = "manRect" Width = "321" Height = "241"
RenderTransform = "{StaticResource InitialMatrixTransform}"
IsManipulationEnabled = "true" Canvas.Left = "-70" Canvas.Top = "-170">
<Rectangle.Fill>
<ImageBrush ImageSource = "Images/DSC_0076.JPG"/>
</Rectangle.Fill>
</Rectangle>
</Canvas>
</Window>
The implementation of code behind:
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WPFMultiTouchInput {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
void Window_ManipulationStarting(object sender, ManipulationStartingEventArgs e) {
e.ManipulationContainer = this;
e.Handled = true;
}
void Window_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) {
Rectangle rectToMove = e.OriginalSource as Rectangle;
Matrix rectsMatrix = ((MatrixTransform)rectToMove.RenderTransform).Matrix;
rectsMatrix.RotateAt(e.DeltaManipulation.Rotation, e.ManipulationOrigin.X, e.ManipulationOrigin.Y);
rectsMatrix.ScaleAt(e.DeltaManipulation.Scale.X, e.DeltaManipulation.Scale.X,
e.ManipulationOrigin.X, e.ManipulationOrigin.Y);
rectsMatrix.Translate(e.DeltaManipulation.Translation.X,
e.DeltaManipulation.Translation.Y);
rectToMove.RenderTransform = new MatrixTransform(rectsMatrix);
Rect containingRect = new Rect(((FrameworkElement)e.ManipulationContainer).RenderSize);
Rect shapeBounds = rectToMove.RenderTransform.TransformBounds(new Rect(rectToMove.RenderSize));
if (e.IsInertial && !containingRect.Contains(shapeBounds)) {
e.Complete();
}
e.Handled = true;
}
void Window_InertiaStarting(object sender, ManipulationInertiaStartingEventArgs e) {
e.TranslationBehavior.DesiredDeceleration = 10.0 * 96.0 / (1000.0 * 1000.0);
e.ExpansionBehavior.DesiredDeceleration = 0.1 * 96 / (1000.0 * 1000.0);
e.RotationBehavior.DesiredDeceleration = 720 / (1000.0 * 1000.0);
e.Handled = true;
}
}
}
But the manipulation events are not get fired.
The sample is from here.
I am using HP TouchSmart610 monitor as my touch device. Is there any specific driver to be installed to work the windows touch events?
[EDIT]
I am working remotely using this HP TouchSmart610 touch device. That is my application is running in another machine. I doubt, this may be a reason. Not sure. Any idea?
You forgot to attached the ManipulationStarting, ManipulationDelta and ManipulationInertiaStarting event handlers to your window:
<Window x:Class = "WPFMultiTouchInput.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local = "clr-namespace:WPFMultiTouchInput"
ManipulationStarting="Window_ManipulationStarting"
ManipulationDelta="Window_ManipulationDelta"
ManipulationInertiaStarting="Window_InertiaStarting"
mc:Ignorable = "d" Title = "MainWindow" Height = "350" Width = "604">
The original sample can be seen here: Walkthrough: Creating Your First Touch Application

WPF dynamic Textbox shows in different positions

This is some interesting behavior and maybe someone could shed some light on it. I have a Grid that is the size of my Screen and is transparent. In that I generate a TextBox dynamically, but for whatever reason that TextBox element slides down on the screen 4 times and then reappears on top again just to do the same again.... Any ideas how I can keep that locked in one place? Btw the same happens when I use a canvas instead of loading it into the grid...
double x_co = Convert.ToDouble(x_coord);
double y_co = Convert.ToDouble(y_coord);
TextBox txtNumber = new TextBox();
txtNumber.Name = "L" + x_coord.ToString();
txtNumber.Height = 85;
txtNumber.Width = 200;
txtNumber.Opacity = 1;
txtNumber.FontSize = 42;
txtNumber.SetValue(TextBlock.FontWeightProperty, FontWeights.Bold);
txtNumber.Background = System.Windows.Media.Brushes.Transparent;
txtNumber.BorderBrush = System.Windows.Media.Brushes.Transparent;
txtNumber.HorizontalAlignment = HorizontalAlignment.Left;
txtNumber.VerticalAlignment = VerticalAlignment.Top;
txtNumber.Foreground = new SolidColorBrush(Colors.White);
txtNumber.Margin = new Thickness(x_co, y_co, 0, 0);
RootGrid.Children.Add(txtNumber);
RootGrid.RegisterName(txtNumber.Name, txtNumber);
string tx_num = "L" + x_coord.ToString();
txtNumber= (TextBox)this.RootGrid.FindName(tx_num);
txtNumber.Text = action;
And thats the XAML I load it into:
<Window x:Name="MyPanel" x:Class="C1.hud"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="hud" Height="1080" Width="1920" AllowsTransparency="True" WindowStyle="None">
<Window.Background>
<SolidColorBrush Opacity="0.0" Color="White"/>
</Window.Background>
<Grid x:Name="RootGrid" Margin="0,0,0,0"/>

StoryBoard always starts with original co-ordinates after Transform in xaml (windows store app)

I have a canvas that has a double animation implemented after it is transformed closer to a specific co-ordinates.
The transform and double animation works fine. But after the animation is complete, the canvas resets into right most corner of the layout. Weird thing is, this behavior does not exist if I do not give any width to the canvas or remove the grid columns.
Any ideas what am I missing here? I am trying to have source on the right of the page and target on the left.
EDIT -1:
After some testing I realized that the values are not resetting, but the storyboard/double animation itself is moving the canvas far right. I do not understand what is causing this though.
EDIT-2:
On further investigation, looks like double animation always starts with initial co-ordinates of the canvas rather than using the current location. Even when from co-ordinates are mentioned relative to whole grid, it only animates based on original layout. Is there a way Force the storyboard to use current location?
Here is the code:
XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Canvas Name="target" >
<Rectangle Width="150" Height="150" Fill="Gray" />
</Canvas>
<Canvas Name="source" ManipulationMode="All" Grid.Column="1">
<Rectangle Name="testRectangle" Width="150" Height="150" Fill="Blue" />
</Canvas>
</Grid>
Code Behind:
Public Point p = new Point(225, 225);
public MainPage()
{
this.InitializeComponent();
DragabbleRectangle();
}
private void DragabbleRectangle()
{
source.ManipulationDelta += source_ManipulationDelta;
source.RenderTransform = new CompositeTransform(); ;
}
void source_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Point origin = new Point(0, 0);
Canvas rect = sender as Canvas;
Point pT= rect.TransformToVisual(Window.Current.Content).TransformPoint(origin);
CompositeTransform tT = rect.RenderTransform as CompositeTransform;
tT.TranslateX += e.Delta.Translation.X;
tT.TranslateY += e.Delta.Translation.Y;
var distance = Math.Pow(pT.X - p.X,2) + Math.Pow(pT.Y-p.Y, 2);
if (distance < 2500)
{
Duration d = TimeSpan.FromSeconds(1);
rect.Margin = new Thickness(0);
rect.RenderTransform = tT;// new CompositeTransform();
DoubleAnimation xAnimation = new DoubleAnimation { To = p.X, From = pT.X,
Duration = d, EasingFunction = new QuinticEase() };
DoubleAnimation yAnimation = new DoubleAnimation { To = p.Y, From = pT.Y,
Duration = d, EasingFunction = new QuinticEase() };
Storyboard.SetTargetProperty(xAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)");
Storyboard.SetTargetProperty(yAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
Storyboard storyboard = new Storyboard();
Storyboard.SetTarget(storyboard, rect);
storyboard.Children.Add(xAnimation);
storyboard.Children.Add(yAnimation);
storyboard.Begin();
}
}
Looks like you are not subscribing to the story board completed event. If this is the case you will have to give the relative distance from the original coordinates. Here is something you can try that should do the trick:
name the root grid as layout
<Grid x:name="layout" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
...
...
</Grid>
in the code behind change the animations to relative distance...
Point relative = layout.TransformToVisual(source).TransformPoint(origin);
DoubleAnimation xAnimation = new DoubleAnimation { To = p.X + relative.X, From = tT.TranslateX,
Duration = d, EasingFunction = new QuinticEase() };
DoubleAnimation yAnimation = new DoubleAnimation { To = p.Y + relative.Y, From = tT.TranslateY,
Duration = d, EasingFunction = new QuinticEase() };

How to add text to the shapes in WPF

I am drawing Circle on an WPF window. The problem is that I am unable add Text to the Circle. The code is given below:
public Graphics()
{
InitializeComponent();
StackPanel myStackPanel = new StackPanel();
Ellipse myel = new Ellipse();
SolidColorBrush mscb = new SolidColorBrush();
mscb.Color = Color.FromArgb(255, 255, 0, 0);
myel.Fill = mscb;
myel.StrokeThickness = 2;
myel.Stroke = Brushes.Black;
myel.Width = 100;
myel.Height = 100;
//string str = "hello";
myStackPanel.Children.Add(myel);
this.Content = myStackPanel;
}
Please help me in this regard.
Shapes are simply shapes, if you want to add text then add both the shape and a TextBlock with your text to a common container which lays them on top of each other, e.g. a Grid without rows or columns.
In XAML:
<Grid>
<Ellipse Width="100" .../>
<TextBlock Text="Lorem Ipsum"/>
</Grid>
C#
var grid = new Grid();
grid.Children.Add(new Ellipse { Width = 100, ... });
grid.Children.Add(new TextBlock { Text = "Lorem Ipsum" });
Or you can use direct positioning in a Canvas if you prefer direct control over the draw position:
My sample defines a UI control that draws rectangles with text in it:
XAML:
<UserControl x:Class="DrawOnCanvas"
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"
xmlns:local="clr-namespace:MySample"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Canvas x:Name="DrawCanvas" Height="30"/>
</Grid>
Code behind:
// You might e.g. call this in the constructor of DrawOnCanvas
internal void DrawRectWithText()
{
var rect = new System.Windows.Shapes.Rectangle();
rect.Stroke = new SolidColorBrush(Colors.Black);
rect.Fill = new SolidColorBrush(Colors.Beige);
rect.Width = 100;
rect.Height = 100;
// Use Canvas's static methods to position the rectangle
Canvas.SetLeft(rect, 100);
Canvas.SetTop(rect, 100);
var text = new TextBlock()
{
Text = task.Title,
};
// Use Canvas's static methods to position the text
Canvas.SetLeft(text, 90);
Canvas.SetTop(text, 90);
// Draw the rectange and the text to my Canvas control.
// DrawCanvas is the name of my Canvas control in the XAML code
DrawCanvas.Children.Add(rect);
DrawCanvas.Children.Add(text);
}

Categories