Application of animations based on .NET framework in industrial automation

This is an example of a WPF desktop application using XAML and C#. Its main goal is to present basic animations using the Storyboard class. All graphics used in the application are vector objects. Their usage in WPF has been handled through external library called SharpVectors (https://github.com/ElinamLLC/SharpVectors)

The application consists of one window with the control buttons in the header, while in the central part there is a symbolic graphic of the Transmission Belt (and Gear Wheels) with a Box on it.

The main container is the Grid control, which contains the definition for 3 rows.

  • In the first row there is another Grid control, on which the control buttons are arranged and the Border control. The Border serves as a container for the Thumb control, which is to enable “drag” functionality for the entire application window.
<Button x:Name="_play" Grid.Column="0" Margin="3" Background="Transparent" Style="{StaticResource Hoverless}" Click="_play_Click" >
	<svgc1:SvgViewbox Source="Resources/play.svg" />
</Button>
<Button x:Name="_stop" Grid.Column="0" Margin="3" Background="Transparent" Style="{StaticResource Hoverless}" Click="_stop_Click"  >
	<svgc1:SvgViewbox  Source="Resources/stop.svg"/>
</Button>
<Button x:Name="_exit" Grid.Column="2" Margin="3" Background="Transparent" Style="{StaticResource Hoverless}" Click="_exit_Click"  >
	<svgc1:SvgViewbox  Source="Resources/exit.svg" />
</Button>

<Border 
	Height="{Binding ActualHeight, ElementName=_header, Mode=OneWay}"
	Width="473"
	Grid.Column="1"
	x:Name="headerBorder" 
	VerticalAlignment="Center"
	HorizontalAlignment="Center" 
	Background="Transparent" 
	BorderThickness="1,1,1,1"
	BorderBrush="Transparent" Margin="5,-5,2,0">
	<Grid>
		<Thumb 
			x:Name="headerThumb" 
			Opacity="0" 
			Background="{x:Null}" 
			Foreground="{x:Null}" 
			DragDelta="headerThumb_DragDelta"/>
	</Grid>
</Border>

  • On the second row of the main Grid control there is a Button control (using the appropriate Style, which hides the button background and the default highlight effect of the button when the mouse is over it). This control is transformed by -140px in the X axis in order to position the Box at the beginning of the Transmission Belt. In addition, the transformation of the position of the control has a given name, so you can refer to it in Code Behind.

To display the Box graphics, instead of the default Button control appearance, the previously mentioned external SharpVectors library is used, to be exact the SvgViewbox control that allows the use of vector graphics.

<Button Grid.Row="1" x:Name="_box" Background="Transparent" Margin="5" Style="{StaticResource CogButton}">
	<Button.RenderTransform>
		<TranslateTransform x:Name="_positionTransform_case" X="-140"/>
	</Button.RenderTransform>
	<svgc1:SvgViewbox Source="/Resources/box.svg"/>
</Button>

  • On the third and last row there are two “overlapping” controls. In order to achieve the effect of belting the Gear Wheels through the Transmission Belt, it was necessary to use the UserControl control with a transparent background effect, in the center of which there is a vector image of the Transmission Belt itself. The Gears are grouped together with the Grid control and, like the Box, have a transformation, but a bit more complex. First of all, they have a transformation of their center of mass that has been moved exactly to the center of the object itself, and they have a named rotation transformation that will be used in Code Behind.
<UserControl x:Name="_belt" Grid.Row="2" Background="Transparent">
	<svgc1:SvgViewbox Source="/Resources/belt.svg"/>
</UserControl>

<Grid x:Name="_conveyor" Grid.Row="2" Background="Transparent" >
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="*"/>
		<ColumnDefinition Width="*"/>
		<ColumnDefinition Width="*"/>
		<ColumnDefinition Width="*"/>
		<ColumnDefinition Width="*"/>
		<ColumnDefinition Width="*"/>
		<ColumnDefinition Width="*"/>
	</Grid.ColumnDefinitions>

	<Button x:Name="_cog1" Grid.Column="1" Margin="15" Style="{StaticResource CogButton}" RenderTransformOrigin=".5,.5">
		<Button.RenderTransform>
			<RotateTransform x:Name="_rotateTransform_cog1"/>
		</Button.RenderTransform>
		<svgc1:SvgViewbox Source="/Resources/cog.svg"/>
	</Button>
	<Button x:Name="_cog2" Grid.Column="2"  Margin="15"  Style="{StaticResource CogButton}" RenderTransformOrigin=".5,.5">
		<Button.RenderTransform>
			<RotateTransform x:Name="_rotateTransform_cog2"/>
		</Button.RenderTransform>
		<svgc1:SvgViewbox Source="/Resources/cog.svg"/>
	</Button>
	<Button x:Name="_cog3" Grid.Column="3"  Margin="15"  Style="{StaticResource CogButton}" RenderTransformOrigin=".5,.5">
		<Button.RenderTransform>
			<RotateTransform x:Name="_rotateTransform_cog3"/>
		</Button.RenderTransform>
		<svgc1:SvgViewbox Source="/Resources/cog.svg"/>
	</Button>
	<Button x:Name="_cog4" Grid.Column="4"  Margin="15"  Style="{StaticResource CogButton}" RenderTransformOrigin=".5,.5">
		<Button.RenderTransform>
			<RotateTransform x:Name="_rotateTransform_cog4"/>
		</Button.RenderTransform>
		<svgc1:SvgViewbox Source="/Resources/cog.svg"/>
	</Button>
	<Button x:Name="_cog5" Grid.Column="5"  Margin="15"  Style="{StaticResource CogButton}" RenderTransformOrigin=".5,.5">
		<Button.RenderTransform>
			<RotateTransform x:Name="_rotateTransform_cog5"/>
		</Button.RenderTransform>
		<svgc1:SvgViewbox Source="/Resources/cog.svg"/>
	</Button>
</Grid>

After starting the animation, the Box moves to the right imitating the movement on the Transmission Belt. The imitation of the Transmission Belt’s movement is shown by the rotating Gear Wheels placed “inside” the Transmission Belt. After the Box reaches the right border of the Belt, the animation of the smooth change of the opacity of the Box takes place, which is supposed to imitate its “disappearance”. Then the Box will appear on the left side of the Belt in the reverse animation – the opacity of the Box is increased until it appears completely.


Now let’s look at Code Behind.

In the constructor of the Main Window of our application, there are methods necessary to initialize the animations.

  • InitializeRotateAnimation is a universal method that has been applied to all Gear Wheels to create a Storyboard object and add a DoubleAnimation animation to that object to rotate the Gears. Additionally, there is a support for the Completed Event, which is performed each time the Storyboard element performs the animation declared on it.
private void InitializeRotateAnimation(ref Button givenButton, ref RotateTransform givenTransform)
{
	Storyboard storyboard = new Storyboard
	{
		Duration = new Duration(TimeSpan.FromSeconds(2))
	};
	DoubleAnimation rotateAnimation = new DoubleAnimation()
	{
		To = givenTransform.Angle + 360,
		Duration = storyboard.Duration
	};

	Storyboard.SetTarget(rotateAnimation, givenButton);
	Storyboard.SetTargetProperty(rotateAnimation, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));

	storyboard.Children.Add(rotateAnimation);

	Resources.Add(givenButton.Name + "_Storyboard", storyboard);

	((Storyboard)Resources[givenButton.Name + "_Storyboard"]).Completed += new EventHandler(myanim_Completed);
}

  • InitializeCaseAnimation is a method that is used to declare an animation for the Box object. The animation of this object actually consists of four animations on four separate Storyboard objects.
    • Animation of the Box moving to the right

//Move Animation from middle to the edge
Storyboard storyboard1 = new Storyboard
{
	Duration = new Duration(TimeSpan.FromSeconds(4))
};
DoubleAnimation moveAnimation = new DoubleAnimation()
{
	To = givenTransform.X + 280,
	From = givenTransform.X,
	Duration = storyboard1.Duration
};

Storyboard.SetTarget(moveAnimation, caseButton);
Storyboard.SetTargetProperty(moveAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));

storyboard1.Children.Add(moveAnimation);

Resources.Add(caseButton.Name + "_Storyboard1", storyboard1);

((Storyboard)Resources[caseButton.Name + "_Storyboard1"]).Completed += new EventHandler(caseAnim_Completed1);

  • Animation of the Box disappearing after reaching the right edge of the Transmission Belt
//Opacity animation on the Right Edge
Storyboard storyboard2 = new Storyboard
{
	Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
DoubleAnimation caseDoneAnmiation = new DoubleAnimation()
{
	To = 0,
	Duration = storyboard2.Duration
};
Storyboard.SetTarget(caseDoneAnmiation, caseButton);
Storyboard.SetTargetProperty(caseDoneAnmiation, new PropertyPath("Opacity"));

storyboard2.Children.Add(caseDoneAnmiation);

Resources.Add(caseButton.Name + "_Storyboard2", storyboard2);

((Storyboard)Resources[caseButton.Name + "_Storyboard2"]).Completed += new EventHandler(caseAnim_Completed2);

  • Animation of the appearance of the Box (after it disappears) on the left side of the Transmission Belt
//Appear Animation - new Case
Storyboard storyboard3 = new Storyboard
{
	Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
DoubleAnimation caseNewAnmiation = new DoubleAnimation()
{
	To = 1,
	Duration = storyboard3.Duration
};
Storyboard.SetTarget(caseNewAnmiation, caseButton);
Storyboard.SetTargetProperty(caseNewAnmiation, new PropertyPath("Opacity"));

storyboard3.Children.Add(caseNewAnmiation);

Resources.Add(caseButton.Name + "_Storyboard3", storyboard3);

((Storyboard)Resources[caseButton.Name + "_Storyboard3"]).Completed += new EventHandler(caseEnd_Completed3);

  • And the actual displacement of the Box to the beginning of the Transmission Belt after reaching the far right side of the Belt
//Transition to Left Edge of the Belt
Storyboard storyboard4 = new Storyboard
{
	Duration = new Duration(TimeSpan.FromMilliseconds(1))
};
DoubleAnimation caseStartAnimation = new DoubleAnimation()
{
	From = givenTransform.X,
	To = givenTransform.X-280,
	Duration = storyboard4.Duration
};
Storyboard.SetTarget(caseStartAnimation, caseButton);
Storyboard.SetTargetProperty(caseStartAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));

storyboard4.Children.Add(caseStartAnimation);

Resources.Add(caseButton.Name + "_Storyboard4", storyboard4);

((Storyboard)Resources[caseButton.Name + "_Storyboard4"]).Completed += new EventHandler(caseStart_Completed4);

After the Animations are properly declared, the only issue is their proper handling, which was done using the Storyboard.Begin, Storyboard.Pause or Storyboard.Resume functions.

Whole solution comes back with open source downloadable files on my GitHub
(https://github.com/JazwinskiMichal/WpfAnimationDemo)

Copyrights 2021 | MIHOW - Realization yprojects.pl