I have a image in my View, which I want to rotate my image 45 degress when a special event happens. But I keep getting this error all the time:
Cannot resolve all property references in the property path
'RenderTransform.Angle'
What type of property path do I need to set to accomplish this?
var dbAscending = new DoubleAnimation(0, 45, new Duration(TimeSpan.FromMilliseconds(1000)));
var storyboard = new Storyboard();
storyboard.Children.Add(dbAscending);
Storyboard.SetTarget(dbAscending, uc.Cross);
Storyboard.SetTargetProperty(dbAscending, new PropertyPath("RenderTransform.Angle"));
storyboard.Begin();
You would have to assign a RotateTransform to your Image's RenderTransform to make your Storyboard work, e.g. like this:
<Image RenderTransformOrigin="0.5,0.5" ...>
<Image.RenderTransform>
<RotateTransform x:Name="imageRotation"/>
</Image.RenderTransform>
</Image>
Although you could animate this with your Storyboard, it might be easier to just start the animation directly on the RotateTransform object:
var rotationAnimation = new DoubleAnimation(45, TimeSpan.FromSeconds(1));
imageRotation.BeginAnimation(RotateTransform.AngleProperty, rotationAnimation);
A RenderTransform has no Angle property. Make sure there is a RotationTransformation assigned to the RenderTranformation property of the element you are rotating.
new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"
If you added the Rotation to a TransformGroup the PropertyPath would be (assuming the Rotation is the first child of the group):
new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"
Related
What I really want is a way to have a negative stroke Thickness value on a WPF shape such as an ellipse, so that the stoke outline paints outwards towards LEFT and TOP of Shape, rather than inside of the shape, over writing my text when I make the thinkness of the stroke too thick... I want the radius of my ellipse to stay constant, but the stroke to grow outwards with increased thinkness, and the LEFT, TOP placement of the shape to remain contant with the inner fill staying the same size and not getting covered up by stroke as it is increased in size.
I tried DropShadowEffect, but its kind of too blurry and not well defined enough...and looks kind of messy... really I just want a solid line going around the outside of the shape...
As you can see from attached picture above, I tried to put shadow around two the ellipses using this code below. the problem is that I want it to be a solid color around the outside like a scaletransform of another ellipse of a different color.
var e = new Ellipse();
DropShadowEffect effect = new DropShadowEffect();
effect.Color =Colors.Orange;
effect.Direction = 0;
effect.BlurRadius = 30;
effect.ShadowDepth = 4;
effect.Opacity=0;
e.Effect = effect;
t.Text = string.Format("abc");
t.Measure(new Size(gwin.XStep, gwin.YStep));
t.Arrange(new Rect(t.DesiredSize));
e.StrokeThickness = 2;
e.Stroke = Brushes.Black;
canvas.Children.Add(e);
canvas.Children.Add(t);
Another possible direction towards solving the problem:
<Ellipse RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
Convert to c# code and place one scaletransform ellipse centered inside another scaled transform ellipse of different colors... not sure how to set it up though.
Solution:
Based on suggestion below. I tried creating a grid, setting the width and height of the grid to the size of my ellipse, then adding two ellipses to the grid with different colors and one with a margin set to -10. and it works perfectly ... just need to place the larger ellipse with margin -10 behind the other ellipse when adding it to the grid...here's what it looks like now..
Solution is in here somewhere:
g = new Grid();
e = new Ellipse();
h = new Ellipse();
t = new TextBlock();
t.HorizontalAlignment = HorizontalAlignment.Center;
t.VerticalAlignment = VerticalAlignment.Center;
t.FontWeight = FontWeights.ExtraBold;
g.Children.Add(h);
g.Children.Add(e);
g.Children.Add(t);
gwin.canvas.Children.Add(g);
t.Text = String.Format("{0}.{1}", x, y);
g.Width = gwin.XStep;
g.Height = gwin.YStep;
Canvas.SetLeft (g, gwin.X1 + gwin.XStep*x*2);
Canvas.SetTop (g, gwin.Y1 + gwin.YStep*y*2);
e.StrokeThickness = 2;
e.Stroke = Brushes.Black;
h.Margin = new Thickness(-10);
You can use double ellipses inside a grid overlaying each other like this:
<Grid Width="100" Height="100">
<Ellipse Fill="Black" Margin="-10"/>
<Ellipse Fill="Red" />
</Grid>
The size of this compound is still 100x100 even though the first ellipse is bigger and rendered out of its boundaries.
You may also use a Path and then do this
I think there is something like border. Or you can draw one elipse and then a second one in smaller that has the background color.
To learn about animation and UI, I'm making a basic WPF/C# application where a user selects the number of vehicles to display then those vehicles (ie images of different vehicles) appear in the canvas and move around.
The WPF is very simple:
<Grid>
<Canvas x:Name="MenuTabCanvas" Visibility="Visible">
<Label x:Name="AnimateDemo" Content="Animate!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="104" Background="#25A0DA" Foreground="White" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Cursor="Hand" MouseDown="AnimateGrid" Canvas.Left="640" Canvas.Top="87"/>
<Canvas x:Name="AnimationCanvas" Canvas.Top="150" Canvas.Left="224" Width="814" Height="489">
</Canvas>
</Grid>
I started with a fade animation using the method described in this post and it works fine. Then I tried the translatetransform as shown below leaving the fade code in comments, and the images appear but nothing moves:
private void AnimateGrid(object sender, EventArgs e)
{
int NumberOfVehicles = 5;
var sb = new Storyboard();
for (int i = 0; i < NumberOfVehicles; i++)
{
//create & add the images to our target canvas
Image Img = getRandomVehicleImage(); //returns image of vehicle
AnimationCanvas.Children.Add(Img);
Canvas.SetTop(Img, 30 + 60 * i); //position image w/in canvas
Canvas.SetLeft(Img, 30 + 80 * i);
//add an animation
DoubleAnimation myAnimation = new DoubleAnimation()
{
// From = 0,
To = 150,
Duration = TimeSpan.FromSeconds(2),
};
Storyboard.SetTarget(myAnimation, Img);
// Storyboard.SetTargetProperty(myAnimation, new PropertyPath(Button.OpacityProperty));
Storyboard.SetTargetProperty(myAnimation, new PropertyPath(TranslateTransform.XProperty));
sb.Children.Add(myAnimation);
}
sb.Begin();
}
I have been able to get it to work with TranslateTransform.BeginAnimation but I would prefer to use the storyboard here.
Why does the translate transform behave differently than the opacity animation and what do I need to do to get it to function as intended?
By default, there is no TranslateTransform applied to a UIElement. So if you want to move an Image, you first have to set its RenderTransform property to a TranslateTransform, and then set the TargetProperty to the correct property path:
Img.RenderTransform = new TranslateTransform();
...
Storyboard.SetTargetProperty(myAnimation, new PropertyPath("RenderTransform.X"));
Alternatively, you could directly animate the TranslateTransform, even without a Storyboard:
var transform = new TranslateTransform();
Img.RenderTransform = transform;
transform.BeginAnimation(TranslateTransform.XProperty, myAnimation);
How can I scroll to specific position inside a scrollviewer?
<ScrollViewer x:Name ="MyScrollView" HorizontalScrollBarVisibility="Hidden" Height="500">
<StackPanel x:Name="ContentsPanel">
<TextBlock x:Name="someTb" Height="50">
</TextBlock>
<TextBlock x:Name="otherTb" Height="100">
</TextBlock>
</StackPanel>
</ScrollViewer>
I am trying to scroll to a specific element in my scrollviewer but I am new to UWP and I can't quite get it right how to do it.
I want to set the scroll position of MyScrollView in the second textblock on an event happening.
A better solution is to use ChangeView instead of ScrollToVerticalOffset/ScrollToHorizontalOffset since the latter is obsolete in Windows 10.
MyScrollView.ChangeView(null, abosulatePosition.Y, null, true);
You can even enable scrolling animation by setting the last parameter to false.
Update
For the sake of completion, I've created an extension method for this.
public static void ScrollToElement(this ScrollViewer scrollViewer, UIElement element,
bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null)
{
var transform = element.TransformToVisual((UIElement)scrollViewer.Content);
var position = transform.TransformPoint(new Point(0, 0));
if (isVerticalScrolling)
{
scrollViewer.ChangeView(null, position.Y, zoomFactor, !smoothScrolling);
}
else
{
scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling);
}
}
So in this case, just need to call
this.MyScrollView.ScrollToElement(otherTb);
I found the answer
var transform = otherTb.TransformToVisual(ContentsPanel);
Point absolutePosition = transform.TransformPoint(new Point(0,0));
MyScrollView.ScrollToVerticalOffset(absolutePosition.Y);
Update
In UWP ScrollToVerticalOffset is obsolete so
MyScrollView.ChangeView(null,absolutePosition.Y,null,true)
should be used instead. https://msdn.microsoft.com/en-us/library/windows/apps/dn252763.aspx
Here is a Video demo of the method described below, implemented.
I used to use the ScrollViewerOffsetMediator, an extension method which relied upon the ScrollToVerticalOffset method to smoothly animate scrolling of the ScrollViewer contents. However, ScrollToVerticalOffset has been deprecated in Windows 10, and although it worked in some earlier releases of Windows 10, it no longer does.
The new ChangeView method does not provide either smooth nor controllable animation of the ScrollViewer contents. So here is the solution that I've found:
Place a Grid within the ScrollViewer. Animate the contents of the grid using a RenderTransform. Use the new ChangeView method to set your final desired vertical and horizontal ScrollViewer positions at the time you set up your animation of the grid contents via the transformation. And in your grid transformation, offset the initial values by the final desired ChangeView offset, so that the animation start reference is corrected for the immediate jump that will be caused by the ChangeView method.
XAML:
<ScrollViewer x:Name="MyScrollView">
<Grid Name="MyGrid">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Grid.RenderTransform>
<!-- Original ScrollViewer Contents Here... -->
</Grid>
</ScrollViewer>
Code:
Public Sub AnimateProperty(Obj As DependencyObject, PropPath As String, StartValue As Double, EndValue As Double, Optional PeriodMS As Integer = 350)
Dim Storya As New Storyboard
Dim DA1 As New DoubleAnimationUsingKeyFrames With {.BeginTime = New TimeSpan(0, 0, 0)}
Storyboard.SetTarget(DA1, Obj)
Storyboard.SetTargetProperty(DA1, PropPath)
Dim ddkf1 As New DiscreteDoubleKeyFrame With {.KeyTime = New TimeSpan(0, 0, 0), .Value = StartValue}
Dim edkf1 As New EasingDoubleKeyFrame With {.Value = EndValue, .KeyTime = New TimeSpan(0, 0, 0, 0, PeriodMS)}
Dim pe1 As New PowerEase With {.EasingMode = EasingMode.EaseIn}
edkf1.EasingFunction = pe1
DA1.KeyFrames.Add(ddkf1)
DA1.KeyFrames.Add(edkf1)
Storya.Children.Add(DA1)
Storya.Begin()
End Sub
Example:
AnimateProperty(MyGrid, "(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)", 1, 1.4, 350)
AnimateProperty(MyGrid, "(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)", 1, 1.4, 350)
AnimateProperty(MyGrid, "(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)", -MyScrollView.VerticalOffset, -120, 350)
MyScrollView.ChangeView(Nothing, 0, Nothing, True)
In this example, no matter what the initial vertical position is of the ScrollView, the contents will be smoothly animated to a fixed vertical position and zoom.
Can anyone explain to me what I am doing wrong here? I am most certainly a beginner at C# and I do not want to leave it up to XAML when it comes to animating this image on the screen.
_sb = new Storyboard();
DoubleAnimation moveXAnim = new DoubleAnimation();
moveXAnim.Duration = TimeSpan.FromMilliseconds(5000);
moveXAnim.From = 0;
moveXAnim.To = 300;
_sb.Children.Add(moveXAnim);
Storyboard.SetTarget(moveXAnim, Person);
Storyboard.SetTargetProperty(moveXAnim, "(Canvas.Left)");
_sb.Begin();
All the examples I find are XAML.
What makes you think it does not work? I assume that Person is a UIElement.
Is your Person element contained inside a Canvas? The example below should work for your code>
<Canvas>
<TextBlock
Text="Hi"
x:Name="Person" />
</Canvas>
I have a path (looks like an oval):
<Path Data="Bla Bla"/>
Now I want to scale the path's width and height to whatever I like. I found a way:
<Grid Width="400" Height="50">
<Viewbox Stretch="Fill">
<Path Data="Bla Bla"/>
</Viewbox>
</Grid>
And this works, but I'm wondering if this is the most efficient way to do this? (I had to introduce a grid and viewbox to do this)
Another way to Scale a Path is to use RenderTransform or LayoutTransform
<Path Data="Bla Bla"
RenderTransformOrigin="0.5, 0.5">
<Path.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
</Path.RenderTransform>
</Path>
just FYI, since ViewBox uses ScaleTransform inside it it's basically just as good performance-wise.
You basically have 3 ways to scale a Path:
Wrap it into a ViewBox
Apply a ScaleTransform
Explicitly set a Width and a Height
Method 1. and 2. will yield the same result, while 3. is slightly different because the shape will change size, but the stroke will keep the original Thickness (so it's not really a zoom).
Method 1. would be appropriate when you have an area of a given size that you want to fill. On the other hand method 2. will be useful to enlarge (or reduce) the path by a given amount, for ex. two times the original size.
You could do it programmaticaly, like
http://social.msdn.microsoft.com/Forums/vstudio/en-US/a0d473fe-3235-4725-aa24-1ea9307752d3/how-to-rendertransform-in-code-behind-c?forum=wpf
kUIWEB:kArrow mArrow = new kUIWEB:kArrow();
mArrow.Width=30;
mArrow.Height=30;
mArrow.RenderTransformOrigin=new Point(0.5, 0.5);
ScaleTransform myScaleTransform = new ScaleTransform();
myScaleTransform.ScaleY = 1;
myScaleTransform.ScaleX = 1;
RotateTransform myRotateTransform = new RotateTransform();
myRotateTransform.Angle = 0;
TranslateTransform myTranslate = new TranslateTransform ();
myTranslate.X = 12;
myTranslate.X = 15;
SkewTransform mySkew = new SkewTransform ();
mySkew.AngleX=0;
mySkew.AngleY=0;
// Create a TransformGroup to contain the transforms
// and add the transforms to it.
TransformGroup myTransformGroup = new TransformGroup();
myTransformGroup.Children.Add(myScaleTransform);
myTransformGroup.Children.Add(myRotateTransform);
myTransformGroup.Children.Add(myTranslate);
myTransformGroup.Children.Add(mySkew);
// Associate the transforms to the object
mArrow.RenderTransform = myTransformGroup;