Animation using discrete values from an array - C# [duplicate] - c#

I have a static loader image in wpf, I can easily use it as a loading gif by using WPFAnimatedGIF nuget package, but it seems like an overkill.
There is only one scenario in my application where I want to display a busy indicator.
There is nothing to trigger, it is a hidden object in my window and upon certain condition it becomes visible. Thus it should always rotate and appear like a normal animated loading gif.
What I have tried so far
<Image RenderTransformOrigin=".5,.5" Width="44" Height="44" Source="BusyIndicator.gif">
<Image.RenderTransform>
<RotateTransform Angle="45" />
</Image.RenderTransform>
</Image>
Image that I am using is

This Style animates the Angle of a RotateTransform in 30 degree steps when the Image element is visible.
<Style TargetType="Image" x:Key="BusyIndicatorStyle">
<Setter Property="Width" Value="44"/>
<Setter Property="Height" Value="44"/>
<Setter Property="Source" Value="BusyIndicator.png"/>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsVisible" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="RenderTransform.Angle">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.1" Value="30"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.2" Value="60"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.3" Value="90"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.4" Value="120"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.5" Value="150"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.6" Value="180"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.7" Value="210"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.8" Value="240"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.9" Value="270"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:1.0" Value="300"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:1.1" Value="330"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:1.2" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
...
<Image Style="{StaticResource BusyIndicatorStyle}" />
In order to avoid using an animation with many DiscreteDoubleKeyFrames, you may derive from DoubleAnimation and add a Step property:
public class DoubleAnimationWithSteps : DoubleAnimation
{
public static readonly DependencyProperty StepProperty = DependencyProperty.Register(
nameof(Step), typeof(double), typeof(DoubleAnimationWithSteps));
public double Step
{
get { return (double)GetValue(StepProperty); }
set { SetValue(StepProperty, value); }
}
protected override double GetCurrentValueCore(
double from, double to, AnimationClock animationClock)
{
var value = base.GetCurrentValueCore(from, to, animationClock);
if (Step > 0d)
{
value = Step * Math.Floor(value / Step);
}
return value;
}
protected override Freezable CreateInstanceCore()
{
return new DoubleAnimationWithSteps();
}
}
You would use it like this:
<Storyboard RepeatBehavior="Forever">
<local:DoubleAnimationWithSteps
Storyboard.TargetProperty="RenderTransform.Angle"
Duration="0:0:1.2" To="360" Step="30"/>
</Storyboard>

I know that this question already has an answer, but there is a different approach. Just use ProgressBar and set IsIndeterminate to True:
<ProgressBar Visibility="{Binding IsBusy, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource MaterialDesignCircularProgressBar}" IsIndeterminate="True" Width="36"/>
The style is from Material Design In XAML Toolkit. There is no need to use GIFs as busy indicators, I made the same mistake some time ago

Related

Make a single method work for multiple WPF elements

I've made a click event/method that alters the opacity and IsEnabled properties of a textbox.
private void EditButton(object sender, RoutedEventArgs e)
{
religionTB.IsEnabled = true;
DoubleAnimation fade = new
DoubleAnimation(1,TimeSpan.FromSeconds(0.2));
religionTB.BeginAnimation(OpacityProperty, fade);
}
In my WPF project, there are multiple textboxes, I'd like to apply this method to all these textboxes without having to list all of them in the method. How would I go by this?
You can achieve this by using a Style. To do so, go to the context of your event handler (Control or Window) and add a DependencyProperty, to flag the enabled mode and bind a ToggleButton (the edit button) to it that sets this property to enable/ disable the controls and to trigger a fade in and fade out animation:
In your control:
public static readonly DependencyProperty IsEditEnabledProperty = DependencyProperty.Register(
"IsEditEnabled",
typeof(bool),
typeof(MainWindow),
new PropertyMetadata(default(bool)));
public bool IsEditEnabled { get { return (bool) GetValue(MainWindow.IsEditEnabledProperty); } set { SetValue(MainWindow.IsEditEnabledProperty, value); } }
In your XAML add the TextBox style and link a ToggleButton to IsEditEnabled:
<Window.Resources>
<Style x:Key="OpacityStyle" TargetType="TextBox">
<Setter Property="Opacity" Value="0" />
<Setter Property="IsEnabled"
Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled}"
Value="True">
<! Fade in animation -->
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
BeginTime="0:0:0"
From="0"
To="1"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<! Fade out animation -->
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
BeginTime="0:0:0"
From="1"
To="0"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<ToggleButton x:Name="EditButton" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled, Mode=TwoWay}" />
<TextBox x:Name="AnimatedTextBox" Style="{StaticResource TextBoxAnimationStyle}" >
<TextBox x:Name="AnotherAnimatedTextBox" Style="{StaticResource TextBoxAnimationStyle}" >
<TextBox x:Name="NonanimatedTextBox" >
</StackPanel>
</Grid>
If you make the Style implicit by removing the x:Key attribute, it will apply to all TextBox elements within the scope

WPF rotate an loading image for gif effect

I have a static loader image in wpf, I can easily use it as a loading gif by using WPFAnimatedGIF nuget package, but it seems like an overkill.
There is only one scenario in my application where I want to display a busy indicator.
There is nothing to trigger, it is a hidden object in my window and upon certain condition it becomes visible. Thus it should always rotate and appear like a normal animated loading gif.
What I have tried so far
<Image RenderTransformOrigin=".5,.5" Width="44" Height="44" Source="BusyIndicator.gif">
<Image.RenderTransform>
<RotateTransform Angle="45" />
</Image.RenderTransform>
</Image>
Image that I am using is
This Style animates the Angle of a RotateTransform in 30 degree steps when the Image element is visible.
<Style TargetType="Image" x:Key="BusyIndicatorStyle">
<Setter Property="Width" Value="44"/>
<Setter Property="Height" Value="44"/>
<Setter Property="Source" Value="BusyIndicator.png"/>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsVisible" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="RenderTransform.Angle">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.1" Value="30"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.2" Value="60"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.3" Value="90"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.4" Value="120"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.5" Value="150"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.6" Value="180"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.7" Value="210"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.8" Value="240"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:0.9" Value="270"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:1.0" Value="300"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:1.1" Value="330"/>
<DiscreteDoubleKeyFrame KeyTime="0:0:1.2" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
...
<Image Style="{StaticResource BusyIndicatorStyle}" />
In order to avoid using an animation with many DiscreteDoubleKeyFrames, you may derive from DoubleAnimation and add a Step property:
public class DoubleAnimationWithSteps : DoubleAnimation
{
public static readonly DependencyProperty StepProperty = DependencyProperty.Register(
nameof(Step), typeof(double), typeof(DoubleAnimationWithSteps));
public double Step
{
get { return (double)GetValue(StepProperty); }
set { SetValue(StepProperty, value); }
}
protected override double GetCurrentValueCore(
double from, double to, AnimationClock animationClock)
{
var value = base.GetCurrentValueCore(from, to, animationClock);
if (Step > 0d)
{
value = Step * Math.Floor(value / Step);
}
return value;
}
protected override Freezable CreateInstanceCore()
{
return new DoubleAnimationWithSteps();
}
}
You would use it like this:
<Storyboard RepeatBehavior="Forever">
<local:DoubleAnimationWithSteps
Storyboard.TargetProperty="RenderTransform.Angle"
Duration="0:0:1.2" To="360" Step="30"/>
</Storyboard>
I know that this question already has an answer, but there is a different approach. Just use ProgressBar and set IsIndeterminate to True:
<ProgressBar Visibility="{Binding IsBusy, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource MaterialDesignCircularProgressBar}" IsIndeterminate="True" Width="36"/>
The style is from Material Design In XAML Toolkit. There is no need to use GIFs as busy indicators, I made the same mistake some time ago

How do I animate a custom components Rotation Transformation?

Given the following :
<Viewbox>
<Foo:Bar
x:FieldModifier="private"
x:Name="fooBar"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5">
<Foo:Bar.RenderTransform>
<TransformGroup>
<ScaleTransform
x:FieldModifier="private"
x:Name="xfScale"/>
<RotateTransform
x:FieldModifier="private"
x:Name="xfRotate"/>
</TransformGroup>
</Foo:Bar.RenderTransform>
<Foo:Bar.Style>
<Style TargetType="{x:Type Foo:Bar}">
<Style.Triggers>
<DataTrigger
Binding="{
Binding Flip,
RelativeSource={
RelativeSource AncestorType={
x:Type local:myFooBar}}}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty=""/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Foo:Bar.Style>
</Foo:Bar>
</Viewbox>
Which is for a new component that is basically a fancy label stuck inside of a ViewBox (for auto-scaling the label), what do I need to point the Storyboard.TargetProperty at to be able to animate, say, the RotateTransform Angle property?
Your TargetName will need to be set for your xfScale / xfRotate named transforms respectfully.
Your TargetProperty will be the properties of the transforms used.
Like for Scale;
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
and
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
Except that only specifies the Property, you still need to provide a Value to animate to. So in it's entirety, it would become something like;
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="xfScale"
Storyboard.TargetProperty="X">
<SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0" />
</DoubleAnimationUsingKeyFrames>
Or for Rotate you need your Angle Property. It's worth mentioning, Blend makes this stuff much quicker/easier to do than by hand, especially for complex animations.
Hope this helps, cheers.

Storyboard TargetProperty binding

I have a Control that can be moved on by dragging. when i drag the control i have a code behind that changes a DependencyProperty that a TranslateTransform is bound to.
now i need to add a button that when is pressed it moves the control, and needs to update the DependencyProperty. I can move the control but can't figure out how to update the DependencyProperty.
code behind:
public partial class AirspeedIndicatorView : UserControl
{
public static readonly DependencyProperty WantedValueProperty =
DependencyProperty.Register("WantedValue", typeof(double), typeof(AirspeedIndicatorView),
new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, WantedPropertyChanged));
public double WantedValue
{
get { return (double)GetValue(WantedValueProperty); }
set { SetValue(WantedValueProperty, value); }
}
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
WantedValue += e.VerticalChange;
}
}
XAML:
<Thumb Canvas.Top="-6" Height="12" Width="16" DragDelta="Thumb_DragDelta" x:Name="WantedThumb">
<Thumb.RenderTransform>
<TranslateTransform Y="{Binding WantedValue,ElementName=View}" />
</Thumb.RenderTransform>
</Thumb>
<Button Padding="1" Margin="1">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="OverridesDefaultStyle" Value="False" />
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="MoveWanted">
<Storyboard Target="{x:Reference WantedThumb}" TargetProperty="RenderTransform.Y" AutoReverse="False">
<DoubleAnimation BeginTime="00:00:00" Duration="0:0:0" By="-1" />
<DoubleAnimation BeginTime="00:00:00.5" Duration="0:0:1.5" By="-15" />
<DoubleAnimation BeginTime="00:00:02" Duration="0:0:1" By="-20" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsPressed" Value="False">
<Trigger.EnterActions>
<StopStoryboard BeginStoryboardName="MoveWanted" />
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
I don't have time to check it now, but you should be able to use the ObjectAnimationUsingKeyFrames class to update your DependencyProperty:
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Saved">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Double>10.0</System:Double>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
Actually, the DoubleAnimationUsingKeyFrames class might be better for you as it is intended to work with doubles, but the only down side is that I don't believe that you can data bind the numerical value, so it would have to be a hard coded value. From the last linked page:
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AnimatedTranslateTransform"
Storyboard.TargetProperty="X"
Duration="0:0:6"
RepeatBehavior="Forever">
<!-- Using a LinearDoubleKeyFrame, the rectangle moves
steadily from its starting position to 500 over
the first 3 seconds. -->
<LinearDoubleKeyFrame Value="500" KeyTime="0:0:3" />
<!-- Using a DiscreteDoubleKeyFrame, the rectangle suddenly
appears at 400 after the fourth second of the animation. -->
<DiscreteDoubleKeyFrame Value="400" KeyTime="0:0:4" />
<!-- Using a SplineDoubleKeyFrame, the rectangle moves
back to its starting point. The
animation starts out slowly at first and then speeds up.
This KeyFrame ends after the 6th
second. -->
<SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="0" KeyTime="0:0:6" />
</DoubleAnimationUsingKeyFrames>

Button Style works at runtime but Visual Studio shows errors at design time

I'm using the following Class and DependencyProperty to allow a style to set an image for a Button:
public static class ImageButton
{
public static readonly DependencyProperty ImageProperty =
DependencyProperty.RegisterAttached("Image", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata((ImageSource)null));
public static ImageSource GetImage(DependencyObject obj)
{
return (ImageSource)obj.GetValue(ImageProperty);
}
public static void SetImage(DependencyObject obj, ImageSource value)
{
obj.SetValue(ImageProperty, value);
}
}
I've defined the following Style (in ImageButton.xaml):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vcontrols="clr-namespace:Vialis.Led.LedControl5.Controls">
<ControlTemplate x:Key="ImageButtonTemplate" TargetType="Button">
<Image Source="{Binding Path=(vcontrols:ImageButton.Image),
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Button}}}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Stretch="Fill"
RenderTransformOrigin="0.5, 0.5">
<Image.Resources>
<Storyboard x:Key="ShrinkStoryboard">
<DoubleAnimation Storyboard.TargetName="ImageScale"
Storyboard.TargetProperty="(ScaleTransform.ScaleX)"
To="0.8"
Duration="0:0:0.15"
AutoReverse="False"/>
<DoubleAnimation Storyboard.TargetName="ImageScale"
Storyboard.TargetProperty="(ScaleTransform.ScaleY)"
To="0.8"
Duration="0:0:0.15"
AutoReverse="False"/>
</Storyboard>
<Storyboard x:Key="GrowStoryboard">
<DoubleAnimation Storyboard.TargetName="ImageScale"
Storyboard.TargetProperty="(ScaleTransform.ScaleX)"
To="1.0"
Duration="0:0:0.15"
AutoReverse="False"/>
<DoubleAnimation Storyboard.TargetName="ImageScale"
Storyboard.TargetProperty="(ScaleTransform.ScaleY)"
To="1.0"
Duration="0:0:0.15"
AutoReverse="False"/>
</Storyboard>
</Image.Resources>
<Image.RenderTransform>
<ScaleTransform x:Name="ImageScale" ScaleX="1" ScaleY="1" CenterX="1" CenterY="1"/>
</Image.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Pressed" Storyboard="{StaticResource ShrinkStoryboard}"/>
<VisualState x:Name="MouseOver" Storyboard="{StaticResource GrowStoryboard}"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Image>
</ControlTemplate>
<Style x:Key="ImageButtonStyle" TargetType="Button">
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Template" Value="{StaticResource ImageButtonTemplate}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
And finally in order to use it I have something like this:
<Button Width="32"
Height="32"
Style="{StaticResource ImageButtonStyle}"
vcontrols:ImageButton.Image="/Images/someimage.png"/>
When I compile and execute the application everything works just fine.
The button gets an image and uses the animations defined in the Style.
At design time however, Visual Studio cannot seem to visualize it and
the XAML editor shows squiggly lines under the entire button definition.
The error information says:
Prefix 'vcontrols' does not map to a namespace.
It's refering to the use of vcontrols in the Style.
If you change the name there, the error will change as well,
so it's not related to the name chosen in the Window/UserControl that is using the Button.
What might be causing this and is there a way to fix it so it works at design time as well?
Update 2:
This issue was only with VS2012 designer (in VS2010 it works fine) and it is already fixed in Visual Studio 2012 Update 3 patch.

Categories