locked
DoubleAnimationUsingKeyFrames, Converters, and Binding RRS feed

  • Question

  • I have several Path elements which I want to animate using DoubleAnimationUsingKeyFrames. The best way I could come up with to do this was to use DoubleAnimationUsingKeyFrames to set the Tag property of the Path and then bind the Data and Visibility properties to the Tag using IValueConverter classes. I have IValueConverter classes that are now working (I tested them by setting the Tag to different static values), but I am having trouble getting the DoubleAnimationUsingKeyFrames to work. I am not sure if it is not starting, if the binding is not getting updated when the value changes, or just something else altogether. Can someone help me figure out what I am missing here? Thanks.

    Nathan Sokalski njsokalski@hotmail.com http://www.nathansokalski.com/

    Wednesday, December 11, 2013 9:35 PM

All replies

  • Some code with explanations of what you're doing would be helpful towards finding a resolution here.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Thursday, December 12, 2013 1:44 PM
    Moderator
  • The 3 main parts of my code are the animation, the converter, and, of course, the Path. The code for the animation and Path (which are in the XAML) are:
    <Page.Triggers>
    	<EventTrigger>
    		<BeginStoryboard>
    			<Storyboard>
    				<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Path3" Storyboard.TargetProperty="Tag">
    					<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="45"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:05.2359878" Value="90"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:06.5449847" Value="135"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:07.8539816" Value="180"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:09.4247780" Value="270"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:10.7581113" Value="290"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:12.3289076" Value="380"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:13.8997040" Value="470"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:14.5663706" Value="480"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:16.1371669" Value="570"/>
    					<LinearDoubleKeyFrame KeyTime="0:0:18.4705002" Value="605"/>
    				</DoubleAnimationUsingKeyFrames>
    			</Storyboard>
    		</BeginStoryboard>
    	</EventTrigger>
    </Page.Triggers>
    <Path Name="Path3" Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" Style="{StaticResource UnderPathStyle}" Data="{Binding Tag,ConverterParameter=90,Converter={StaticResource BowlineToDataConverter},RelativeSource={RelativeSource Mode=Self}}" Tag="45"/>
    The converter is defined using the following code:
    Public Class BowlineToDataConverter : Implements IValueConverter
    	Public Function Convert(value As Object, targetType As Type, parameter As Object, language As String) As Object Implements IValueConverter.Convert
    		If value IsNot Nothing Then System.Diagnostics.Debug.WriteLine(CDbl(value))
    		If CDbl(value) >= CDbl(parameter) Then 'parameter=value at end of segment
    			'Return the complete segment if the value has made it past this segment
    			Select Case CDbl(parameter)
    				Case 90 : Return App.CreateArcSegmentPathGeometry(New Point(94, 229), 100, 100, True, SweepDirection.Counterclockwise, New Point(165, 200))
    				Case 135 : Return App.CreateArcSegmentPathGeometry(New Point(165, 200), 25, 25, False, SweepDirection.Clockwise, New Point(165 - 25 * Math.Cos(Math.PI / 4), 175 + 25 * Math.Sin(Math.PI / 4)))
    				Case 180 : Return App.CreateArcSegmentPathGeometry(New Point(165 - 25 * Math.Cos(Math.PI / 4), 175 + 25 * Math.Sin(Math.PI / 4)), 25, 25, False, SweepDirection.Clockwise, New Point(140, 175))
    				Case 270 : Return App.CreateArcSegmentPathGeometry(New Point(140, 175), 15, 15, False, SweepDirection.Counterclockwise, New Point(125, 160))
    				Case 290 : Return App.CreateLineSegmentPathGeometry(New Point(125, 160), New Point(105, 160))
    				Case 380 : Return App.CreateArcSegmentPathGeometry(New Point(105, 160), 15, 15, False, SweepDirection.Counterclockwise, New Point(90, 175))
    				Case 470 : Return App.CreateArcSegmentPathGeometry(New Point(90, 175), 15, 15, False, SweepDirection.Counterclockwise, New Point(105, 190))
    				Case 480 : Return App.CreateLineSegmentPathGeometry(New Point(105, 190), New Point(115, 190))
    				Case 570 : Return App.CreateArcSegmentPathGeometry(New Point(115, 190), 15, 15, False, SweepDirection.Clockwise, New Point(130, 205))
    				Case 605 : Return App.CreateLineSegmentPathGeometry(New Point(130, 205), New Point(130, 240))
    				Case Else : Return Nothing
    			End Select
    		Else
    			'Return a partial segment if this is the current segment or Nothing for future segments, which will not be visible
    			Select Case CDbl(parameter)
    				Case 90 : If CDbl(value) >= 45 Then Return App.CreateArcSegmentPathGeometry(New Point(94, 229), 100, 100, True, SweepDirection.Counterclockwise, New Point(165 + 100 * Math.Cos(CDbl(value).ToRadians()), 300 - 100 * Math.Sin(CDbl(value).ToRadians()))) Else Return Nothing
    				Case 135 : If CDbl(value) >= 90 Then Return App.CreateArcSegmentPathGeometry(New Point(165, 200), 25, 25, False, SweepDirection.Clockwise, New Point(165 + 25 * Math.Cos(CDbl(value).ToRadians()), 175 + 25 * Math.Sin(CDbl(value).ToRadians()))) Else Return Nothing
    				Case 180 : If CDbl(value) >= 135 Then Return App.CreateArcSegmentPathGeometry(New Point(165 - 25 * Math.Cos(Math.PI / 4), 175 + 25 * Math.Sin(Math.PI / 4)), 25, 25, False, SweepDirection.Clockwise, New Point(165 + 25 * Math.Cos(CDbl(value).ToRadians()), 175 + 25 * Math.Sin(CDbl(value).ToRadians()))) Else Return Nothing
    				Case 270 : If CDbl(value) >= 180 Then Return App.CreateArcSegmentPathGeometry(New Point(140, 175), 15, 15, False, SweepDirection.Counterclockwise, New Point(125 + 15 * Math.Cos((CDbl(value) - 180).ToRadians()), 175 - 15 * Math.Sin((CDbl(value) - 180).ToRadians()))) Else Return Nothing
    				Case 290 : If CDbl(value) >= 270 Then Return App.CreateLineSegmentPathGeometry(New Point(125, 160), New Point(CDbl(value) - 145, 160)) Else Return Nothing
    				Case 380 : If CDbl(value) >= 290 Then Return App.CreateArcSegmentPathGeometry(New Point(105, 160), 15, 15, False, SweepDirection.Counterclockwise, New Point(105 + 15 * Math.Cos((CDbl(value) - 200).ToRadians()), 160 + 15 * Math.Sin((CDbl(value) - 20).ToRadians()))) Else Return Nothing
    				Case 470 : If CDbl(value) >= 380 Then Return App.CreateArcSegmentPathGeometry(New Point(90, 175), 15, 15, False, SweepDirection.Counterclockwise, New Point(105 + 15 * Math.Cos((CDbl(value) - 200).ToRadians()), 175 + 15 * Math.Sin((CDbl(value) - 380).ToRadians()))) Else Return Nothing
    				Case 480 : If CDbl(value) >= 470 Then Return App.CreateLineSegmentPathGeometry(New Point(105, 190), New Point(CDbl(value) - 365, 190)) Else Return Nothing
    				Case 570 : If CDbl(value) >= 480 Then Return App.CreateArcSegmentPathGeometry(New Point(115, 190), 15, 15, False, SweepDirection.Clockwise, New Point(115 + 15 * Math.Cos((CDbl(value) - 210).ToRadians()), 205 + 15 * Math.Sin((CDbl(value) - 210).ToRadians()))) Else Return Nothing
    				Case 605 : If CDbl(value) >= 570 Then Return App.CreateLineSegmentPathGeometry(New Point(130, 205), New Point(130, CDbl(value) - 365)) Else Return Nothing
    				Case Else : Return Nothing
    			End Select
    		End If
    	End Function
    
    	Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, language As String) As Object Implements IValueConverter.ConvertBack
    		Return value
    	End Function
    End Class
    The CreateArcSegmentPathGeometry and CreateLineSegmentPathGeometry functions are defined in the App class as follows:
    Public Shared Function CreateArcSegmentPathGeometry(start As Point, w As Double, h As Double, islarge As Boolean, sweepdir As SweepDirection, endpt As Point) As PathGeometry
    	Dim pg As New PathGeometry()
    	Dim arcseg As New ArcSegment()
    	pg.Figures.Add(New PathFigure())
    	pg.Figures(0).StartPoint = start
    	arcseg.IsLargeArc = islarge
    	arcseg.Point = endpt
    	arcseg.RotationAngle = 0
    	arcseg.Size = New Size(w, h)
    	arcseg.SweepDirection = sweepdir
    	pg.Figures(0).Segments.Add(arcseg)
    	Return pg
    End Function
    Public Shared Function CreateLineSegmentPathGeometry(start As Point, endpt As Point) As PathGeometry
    	Dim pg As New PathGeometry()
    	Dim lineseg As New LineSegment()
    	pg.Figures.Add(New PathFigure())
    	pg.Figures(0).StartPoint = start
    	lineseg.Point = endpt
    	pg.Figures(0).Segments.Add(lineseg)
    	Return pg
    End Function
    The UnderPathStyle (although I strongly doubt that it is related to the problem) is defined in the App.xaml resources as:
    <Style x:Key="BasePathStyle" TargetType="Path">
    	<Setter Property="IsHitTestVisible" Value="False"/>
    	<Setter Property="Stroke" Value="Black"/>
    	<Setter Property="StrokeEndLineCap" Value="Round"/>
    	<Setter Property="StrokeStartLineCap" Value="Flat"/>
    	<Setter Property="StrokeLineJoin" Value="Round"/>
    	<Setter Property="StrokeDashCap" Value="Triangle"/>
    </Style>
    <Style x:Key="UnderPathStyle" TargetType="Path" BasedOn="{StaticResource BasePathStyle}">
    	<Setter Property="Stroke" Value="Black"/>
    	<Setter Property="StrokeThickness" Value="10"/>
    </Style>
    
    What I am basically trying to do is "draw" the Path. Thank you for your help.





    Nathan Sokalski njsokalski@hotmail.com http://www.nathansokalski.com/

    Thursday, December 12, 2013 5:38 PM