I am using UWP and working with the Composition API to programmatically slide items around on the screen and I have found that when the application first starts that the initial slide for many of the elements are not performing correctly. Upon further inspection I have found that the ElementVisual's Offset properties that I use for computing and setting both the initial and destination positions of the animation are sometimes coming up with all zero values for the X,Y,Z values on the very first animation. Best I can tell all animations of that element from that point forward are working correctly as long as the app keeps running.
The app I am working on is more complex and interesting, but I have created a simplified test application to demonstrate the problem I am encountering.
From a new blank UWP project I add the following GridView and Button to the root grid of the page:
<GridView x:Name="TestGridView" HorizontalAlignment="Center" VerticalAlignment="Center">
<GridViewItem >
<Border Width="125" Height="125">
<Grid>
<TextBlock Text="Content String 1" />
</Grid>
</Border>
</GridViewItem>
<GridViewItem>
<Border Width="125" Height="125">
<Grid>
<TextBlock Text="Content String 2" />
</Grid>
</Border>
</GridViewItem>
<GridViewItem>
<Border Width="125" Height="125">
<Grid>
<TextBlock Text="Content String 3"/>
</Grid>
</Border>
</GridViewItem>
</GridView>
<Button Height="125" Width="125" Click="Button_Click"/>
In the codebehind file I add the following additional using statements, a variable declaration and this event handler:
using System.Numerics;
using Windows.UI.Composition;
using Windows.UI.Xaml.Hosting;
using System.Diagnostics;
Compositor compositor;
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (var element in TestGridView.ItemsPanelRoot.Children)
{
var slideVisual = ElementCompositionPreview.GetElementVisual(element);
Debug.WriteLine("Element Offset: " + slideVisual.Offset);
var slideAnimation = compositor.CreateVector3KeyFrameAnimation();
slideAnimation.InsertKeyFrame(0f, slideVisual.Offset);
slideAnimation.InsertKeyFrame(1f, new Vector3(slideVisual.Offset.X, slideVisual.Offset.Y + 20f, 0f));
slideAnimation.Duration = TimeSpan.FromMilliseconds(800);
slideVisual.StartAnimation(nameof(slideVisual.Offset), slideAnimation);
}
}
I also initialize the compositor in the MainPage() constructor:
public MainPage()
{
this.InitializeComponent();
compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
}
I then run the project in Debug mode from Visual Studio. Pressing the button produces the following output in my Immediate Window (separator lines added in editing for clarity)
Element Offset: <0, 0, 0>
Element Offset: <0, 0, 0>
Element Offset: <0, 0, 0>
Element Offset: <0, 20, 0>
Element Offset: <129, 0, 0>
Element Offset: <258, 0, 0>
Element Offset: <0, 40, 0>
Element Offset: <129, 20, 0>
Element Offset: <258, 20, 0>
Element Offset: <0, 60, 0>
Element Offset: <129, 40, 0>
Element Offset: <258, 40, 0>
Note that the first of the three elements does animate on the first slide, but the other two do not and that their Offset values are all zero values.
On each slide there after all three elements animate from their previous position as they should, although the side effects from the first slide animation linger on.
I have tried unsuccessfully to find a way to force the elements in our application to update their Offset values so that the initial slide animation for each element works correctly including doing things like inserting a dummy WarmUp animation just before the actual intended animation that very quickly tells the element to either move from its current position to the same position, or a very slightly modified start position to its actual current position.
var warmUpAnimation = compositor.CreateVector3KeyFrameAnimation();
warmUpAnimation.InsertKeyFrame(0.0f, new Vector3(slideVisual.Offset.X + 1, slideVisual.Offset.Y, slideVisual.Offset.Z));
warmUpAnimation.InsertKeyFrame(1f, slideVisual.Offset);
warmUpAnimation.Duration = TimeSpan.FromMilliseconds(1);
I am guessing this is a bug in the Windows Composition API but I am hoping someone can suggest a simple and effective work around that will force the ElementVisual to correctly initialize after load.
I keep running into this same problem when using the Composition api whenever I use it in UWP applications. For whatever reason initial calls to GetElementVisual for UIElements often do not provide correct return results.
I have resorted to running a test in a loop before using visuals for the first time.
As a simple example, if you add the following function to the above sample:
And then insert the following while loop at the beginning of the foreach segment in the Button_Click routine and add "async" to the click handler:
The visuals animate more or less as they should. However, there does still seem to be a slight delay on the last two elements on the first pass of animations.