I have simple graphic images which I would like to transform on triggered events. Transform means change the width or move it to another position.
For now I use the Image element of the toolbox and Animations via Storyboard, for instance DoubleAnimation or ThicknessAnimation.
However the following issues arise:
the images flickers when changing the width
the image quality varies, does WPF support vector graphics?
Regarding 1. my question is, if other animations should be used.
So I tried the Transformation, see the code :
<Image Height="150" HorizontalAlignment="Left" Margin="12,0,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Source="images/side_view.jpg" Width="1244">
<Image.RenderTransform>
<ScaleTransform x:Name="Minimize" ScaleX="1.0" ScaleY="1.0"/>
</Image.RenderTransform>
</Image>
<Button Content="Next Train" Height="23" HorizontalAlignment="Left" Margin="528,233,0,0" Name="btnNext" VerticalAlignment="Top" Width="75" />
<Grid.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="btnNext">
<BeginStoryboard>
<Storyboard TargetName="Minimize" TargetProperty="ScaleX">
<DoubleAnimation To="0.65" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Works the same as the Animation I applied to width and margin. However, it still flickers! Are there any reasonable explanations?
If you're animating the Width of an image, you're making WPF re render the image, and create it from scatch each time. It goes through both the Layout process and the Rendering process, which makes it flicker and not work as best as it could be.
The best option here is to animate a ScaleTransform. ScaleTransform is done completely in hardware via DirectX and therefor will be extremlely fast, it won't flicker, and the image quality should stay pretty much the same the whole way. (unless ofcourse you are resizing it substantially in which case you'll lose percision.)
Related
I'm attempting to build an odometer like effect in UWP where when a number is incremented it slides up and dissappears while the incremented number appears by sliding up from the bottom (in a very similar mannor to how the Odometer JS library works).
I have the number contained in a text block, that is animating properly.
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
Storyboard.TargetName="Digit1">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="-80" />
</DoubleAnimationUsingKeyFrames>
Currently, when the number moves up it is still completely visible. I want the TextBlock to be "clipped" to its containing Canvas so that it progressively dissappears as it moves outside the bounds of the Canvas.
I have tried both clipping the TextBlock to the Canvas and visa versa, neither of which had the desired effect.
<Canvas x:Name="Odometer"
Clip="{Binding Clip, ElementName=Digit1}">
<TextBlock x:Name="Digit1"
FontSize="100"
Text="8"
Canvas.Left="-104"
Canvas.Top="-30"
RenderTransformOrigin="0.5,0.5"
Clip="{Binding Clip, ElementName=Odometer}">
<TextBlock.RenderTransform> <CompositeTransform /> </TextBlock.RenderTransform>
</TextBlock>
</Canvas>
I do not need to use a Canvas, it's just what I was playing around with. I'm still very new to UWP, so any help would be appreciated.
If you refer to UIElement.Clip:
The clipping geometry for UIElement.Clip in the Windows Runtime API must be a RectangleGeometry.
So, if you want to give a outline for canvas, you need to specify a RectangleGeometry for your canvas.clip, which take a rect of your canvas's actual size.
You can achieve this progrmmatically:
private void Odometer_Loaded(object sender, RoutedEventArgs e)
{
RectangleGeometry rectangle = new RectangleGeometry();
rectangle.Rect = new Rect(0, 0, Odometer.ActualWidth, Odometer.ActualHeight);
Odometer.Clip = rectangle;
}
And don't forget to remove the clip in xaml:
<Canvas x:Name="Odometer" Loaded="Odometer_Loaded">
<TextBlock x:Name="Digit1"
FontSize="100"
Text="8"
Canvas.Left="104"
Canvas.Top="10"
RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<CompositeTransform />
</TextBlock.RenderTransform>
</TextBlock>
</Canvas>
Im trying to animate a image that moves from the right side of the screen to the left, i guess thats what translate is for, but not really sure how it works, or whats the best solution. So far i have this:
<Image Height="50" Width="50" Source="/Assets/Img/cloud.png" Stretch="Uniform">
<Image.RenderTransform>
<TranslateTransform x:Name="p1Translate" X="0" Y="0"/>
</Image.RenderTransform>
</Image>
Which is nothing!
So can someone help me, translate the cloud.png just in the X axis?
Greets,
José Correia
Did you try using the CompositeTransform for your image? You could use Blend to come up with a storyboard animation.
XAML storyboard animation moving images from outside viewport-windows phone 8
For further reference:
Images Animation By Using Story Board in Windows Phone 8/8.1
I believe this article should answer all your questions: Quickstart: Animations for Windows Phone
While you can use the RenderTransform in order to position objects relative to where the layouting engine puts them the easier way is to place the Control you want to move around inside a Canvas which directly attaches position properties to that Control. This is shown in one of the samples in the article I linked to.
Thanks for all the help, heres how i did it:
<phone:PhoneApplicationPage.Resources>
<Storyboard x:Name="Storyboard1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="image">
<EasingDoubleKeyFrame KeyTime="0" Value="-90"/>
<EasingDoubleKeyFrame KeyTime="0:0:8" Value="-598.826"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</phone:PhoneApplicationPage.Resources>
<Image x:Name="image" Source="/Assets/Img/bg_cloud.png" Stretch="Fill" RenderTransformOrigin="50,50" Width="70" Height="40" Margin="497,596,-87,164">
<Image.RenderTransform>
<CompositeTransform/>
</Image.RenderTransform>
</Image>
And Blend really helped me out!
Best regards,
José Correia
I am using WPF for my C# application on which i have several labels in which the texts don't fit. What I want is to make my text move inside the label so that the user can see the entire text.
Example : let's say that my text is 20 characters long, by my label has enough space for only 15 characters. What I want is that my label to display the first 15 characters (characters 1-15) then after 1 second the same characters without the first but with another at the end (so characters 2-16) then the next (characters 3-17) and so on until the last 15 characters (characters 5-20) and then I want them to start from the beginning (the characters 1-15 again).
How can I do that ? One way is (obviously) to use a Timer, but I am sure that a more elegant solution exists.
Below is code for a quick-and-dirty scrolling control that should do what you want as soon as the user mouses over the control.
<Canvas Name="brd"
ClipToBounds="True"
Margin="10"
Height="20" Width="150"
Background="White"
HorizontalAlignment="Left">
<StackPanel Name="spl1"
Orientation="Horizontal"
Canvas.Left="0">
<TextBlock Name="tbk1"
Margin="10,0"
MinWidth="{Binding ElementName=brd,Path=ActualWidth}"
Text="A display of test text that is wider than the control."/>
<TextBlock MinWidth="{Binding ElementName=brd,Path=ActualWidth}"
Margin="10,0"
Text="{Binding ElementName=tbk1,Path=Text}"/>
</StackPanel>
<Canvas.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Name="scroll">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation To="-200" Duration="0:0:4"
Storyboard.TargetName="spl1"
Storyboard.TargetProperty="(Canvas.Left)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<StopStoryboard BeginStoryboardName="scroll"/>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
There is one part that you will need to change for proper operation, that I did not implement. The <DoubleAnimation To="-200"... will need to be changed. Ideally the value for To will be the negative of the ActualWidth property of the tbk1 control. This will require both an element binding to get the value and a ValueConverter to make that value negative.
If you don't want to go to the trouble of a converter, you can just make the To value sufficiently large to accommodate the longest text that you expect. That will get you reasonably close.
You can, of course, tweak this more to provide a smoother transition at the end of one animation cycle and the start of the next.
EDIT
OK, I couldn't leave it half finished. Below is the updated XAML for smooth operation, and the included code for the ValueConverter.
<Window.Resources>
<Converters:ChangeSignConverter x:Key="ChangeSignConverter"/>
</Window.Resources>
<Canvas Name="brd"
ClipToBounds="True"
Margin="10"
Height="20" Width="150"
Background="White"
HorizontalAlignment="Left">
<StackPanel Name="spl1"
Margin="5,0,0,0"
Orientation="Horizontal"
Canvas.Left="0">
<TextBlock Name="tbk1"
Padding="0,0,10,0"
MinWidth="{Binding ElementName=brd,Path=ActualWidth}"
Text="A display of test text that is wider than the control."/>
<TextBlock MinWidth="{Binding ElementName=brd,Path=ActualWidth}"
Text="{Binding ElementName=tbk1,Path=Text}"/>
</StackPanel>
<Canvas.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Name="scroll">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation To="{Binding ElementName=tbk1,Path=ActualWidth,Converter={StaticResource ChangeSignConverter}}"
Duration="0:0:4"
Storyboard.TargetName="spl1"
Storyboard.TargetProperty="(Canvas.Left)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<StopStoryboard BeginStoryboardName="scroll"/>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
Converter class:
class ChangeSignConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert.ToDouble(value) * -1;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert.ToDouble(value) * -1;
}
}
You could also use the WPF animation functionality. You create a custom control which has a TextBlock inside. Define the control's width (which would be smaller than the textblock's width. Then define the storyboard and double animation to animate it however you want it (slower / faster, only animate to the right, animate right then left and so on).
This is just an idea. I'm currently in a rush, but I can provide you with additional code, if you need it.
I would also use Timer for functionality you described. I think there are not many other solutions, if you insist on changing Label in some specified time interval. On the other hand, I would probably use ToolTip, which I think is more intuitive for this.
It's a little hard to describe but I'll try my best.
I have a control which has an image and a label and it needs to have 2 states ("Big", and "Small").
On the "Big" state the image should be centered at the top of the control, and the label should be center below (Just like a dock with an image and a label docked to the top).
On the "Small" state the image should be smaller and at the top left of the control, and the label should be right next to it.
The big state should look like so:
And the small state:
And the tricky part: when I switch between them I need it to animate over 0.3s.
There is no panel I found suitable for this.
DockPanel is a good solution for both of these states, but it can't animate it.
Canvas can animate it, but doesn't have a proper layout (can't center them so easily).
What would be the best way to do it?
In WPF no animation alignment, the only thing that can come up - it ThicknessAnimation. But you can use the DiscreteObjectKeyFrame to set the alignment. Below is a simple demonstration in which to Label set VerticalAlignment in Bottom:
<Grid>
<Grid.Triggers>
<EventTrigger SourceName="Small" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Test" Storyboard.TargetProperty="VerticalAlignment">
<DiscreteObjectKeyFrame KeyTime="0:0:0">
<DiscreteObjectKeyFrame.Value>
<VerticalAlignment>Bottom</VerticalAlignment>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Label x:Name="Test" Content="Test" Width="300" Height="300" Background="Aqua" VerticalAlignment="Top" HorizontalAlignment="Center" />
<Button Name="Small" Content="Small" Width="100" Height="30" HorizontalAlignment="Right" VerticalAlignment="Top" />
</Grid>
Using it in combination with standard animations, such as DoubleAnimation, I think you'll be able to achieve this goal.
I have a DropShadowEffect on a Rectangle with a fill that's slightly transparent. The problem is, the drop shadow also shows up inside the fill, which is what I don't want. Anyways to solve this? I tried this, but it doesn't work for me :/
This is very tricky.
Okay, I don't know the exact answer. But here is what will give you almost the desired effect. Try it.
This is a sample Grid with yellow background. I've drawn two intersecting rectangles of 100x100 dimension on it. You may need to customize the size according to your need. One rectangle is gray rectangle (to show shadow), and the other is a red rectangle (the actual semi-transparent rectangle that you want to display). The shadow depth has been hard coded as 5 pixels here. Please customize it at:
RectangleGeometry Rect="5,5,100,100"
RectangleGeometry Rect="0,0,95,95"
So, the grid looks like:
<Grid Background="Yellow">
<!-- A rectangle for shadow -->
<Rectangle Fill="Gray" Width="100" Height="100" Opacity=".7">
<Rectangle.Clip>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="5,5,100,100"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="0,0,95,95"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Rectangle.Clip>
<Rectangle.Effect>
<!-- For nice soft shadow effect -->
<BlurEffect Radius="5" />
</Rectangle.Effect>
</Rectangle>
<!-- Actual rectangle which is translucent -->
<Rectangle Fill="Red" Width="100" Height="100" Opacity=".6" >
<Rectangle.Clip>
<RectangleGeometry Rect="0,0,95,95"/>
</Rectangle.Clip>
</Rectangle>
</Grid>
Update (8-Nov-11):
You can replace the hard-coded width and height by binding them to parent's width and height. Check this SO topic for multiple binding which you will need. More study material on binding: here.
An example of how the XAML snippet will look like is:
<Rectangle Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"
Height="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}">
</Rectangle>
Since I'm not the expert on data binding, you need to research on your own from here. I feel that you will need your own value-converters for assigning special width ad height (ActualWidth - ShadowDepth kind of stuff (ShadowDepth being 5 pixels here)).