Skip to content

Commit c47b47b

Browse files
committed
Fixed VisualExtensions.Translation underlying target property
1 parent 319b735 commit c47b47b

File tree

2 files changed

+74
-14
lines changed

2 files changed

+74
-14
lines changed

Microsoft.Toolkit.Uwp.UI/Extensions/VisualExtensions.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Globalization;
56
using System.Numerics;
67
using Windows.UI.Composition;
78
using Windows.UI.Xaml;
@@ -115,10 +116,10 @@ public static void SetOffset(DependencyObject obj, string value)
115116
}
116117

117118
/// <summary>
118-
/// Gets the <see cref="Visual.TransformMatrix"/>.<see cref="Matrix4x4.Translation"/> property of a <see cref="UIElement"/> in a <see cref="string"/> form.
119+
/// Gets the <c>"Translation"</c> property of the underlying <see cref="Visual"/> object for a <see cref="UIElement"/>, in <see cref="string"/> form.
119120
/// </summary>
120121
/// <param name="obj">The <see cref="DependencyObject"/> instance.</param>
121-
/// <returns>A <see cref="Vector3"/> <see cref="string"/> representation of the <see cref="Visual.TransformMatrix"/>.<see cref="Matrix4x4.Translation"/> property.</returns>
122+
/// <returns>The <see cref="string"/> representation of the <c>"Translation"</c> property property.</returns>
122123
public static string GetTranslation(DependencyObject obj)
123124
{
124125
if (!DesignTimeHelpers.IsRunningInLegacyDesignerMode && obj is UIElement element)
@@ -130,10 +131,10 @@ public static string GetTranslation(DependencyObject obj)
130131
}
131132

132133
/// <summary>
133-
/// Sets the <see cref="Visual.TransformMatrix"/>.<see cref="Matrix4x4.Translation"/> property of a <see cref="UIElement"/> in a <see cref="string"/> form.
134+
/// Sets the <c>"Translation"</c> property of the underlying <see cref="Visual"/> object for a <see cref="UIElement"/>, in <see cref="string"/> form.
134135
/// </summary>
135136
/// <param name="obj">The <see cref="DependencyObject"/> instance.</param>
136-
/// <param name="value">The <see cref="string"/> representation of the <see cref="Vector3"/> to be set.</param>
137+
/// <param name="value">The <see cref="string"/> representation of the <c>"Translation"</c> property property to be set.</param>
137138
public static void SetTranslation(DependencyObject obj, string value)
138139
{
139140
if (!DesignTimeHelpers.IsRunningInLegacyDesignerMode && obj is UIElement element)
@@ -549,19 +550,33 @@ private static void SetOffsetForElement(string value, UIElement element)
549550

550551
private static string GetTranslationForElement(UIElement element)
551552
{
552-
return GetVisual(element).TransformMatrix.Translation.ToString();
553+
CompositionGetValueStatus result = GetVisual(element).Properties.TryGetVector3("Translation", out Vector3 translation);
554+
555+
return result switch
556+
{
557+
// The ("G", CultureInfo.InvariantCulture) combination produces a string with the default numeric
558+
// formatting style, and using ',' as component separator, so that the resulting text can safely
559+
// be parsed back if needed with the StringExtensions.ToVector3(string) extension, which uses
560+
// the invariant culture mode by default so that the syntax will always match that from XAML.
561+
CompositionGetValueStatus.Succeeded => translation.ToString("G", CultureInfo.InvariantCulture),
562+
_ => "<0, 0, 0>"
563+
};
553564
}
554565

555566
private static void SetTranslationForElement(string value, UIElement element)
556567
{
557568
ElementCompositionPreview.SetIsTranslationEnabled(element, true);
558569

559-
Visual visual = GetVisual(element);
560-
Matrix4x4 transform = visual.TransformMatrix;
561-
562-
transform.Translation = value.ToVector3();
563-
564-
visual.TransformMatrix = transform;
570+
// The "Translation" attached property refers to the "hidden" property that is enabled
571+
// through "ElementCompositionPreview.SetIsTranslationEnabled". The value for this property
572+
// is not available directly on the Visual class and can only be accessed through its property
573+
// set. Note that this "Translation" value is not the same as Visual.TransformMatrix.Translation.
574+
// In fact, the latter doesn't require to be explicitly enabled and is actually combined with
575+
// this at runtime (ie. the whole transform matrix is combined with the additional translation
576+
// from the "Translation" property, if any), and the two can be set and animated independently.
577+
// In this case we're just interested in the "Translation" property, which is more commonly used
578+
// as it can also be animated directly with a Vector3 animation instead of a Matrix4x4 one.
579+
GetVisual(element).Properties.InsertVector3("Translation", value.ToVector3());
565580
}
566581

567582
private static double GetOpacityForElement(UIElement element)

UnitTests/UnitTests.UWP/UI/Extensions/Test_VisualExtensions.cs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,28 @@
88
using Windows.UI.Xaml.Controls;
99
using Microsoft.Toolkit.Uwp.UI;
1010
using System.Numerics;
11+
using Windows.UI.Composition;
12+
using Microsoft.Toolkit.Uwp.UI.Animations;
1113

1214
namespace UnitTests.UWP.UI
1315
{
1416
[TestClass]
1517
[TestCategory("Test_VisualExtensions")]
1618
public class Test_VisualExtensions : VisualUITestBase
1719
{
20+
[TestMethod]
21+
public async Task GetDefaultTranslation()
22+
{
23+
await App.DispatcherQueue.EnqueueAsync(() =>
24+
{
25+
var button = new Button();
26+
27+
string text = VisualExtensions.GetTranslation(button);
28+
29+
Assert.AreEqual(text, "<0, 0, 0>");
30+
});
31+
}
32+
1833
[TestMethod]
1934
public async Task SetAndGetTranslation()
2035
{
@@ -27,11 +42,41 @@ await App.DispatcherQueue.EnqueueAsync(async () =>
2742

2843
await SetTestContentAsync(grid);
2944

30-
Assert.AreEqual(button.GetVisual().TransformMatrix.Translation, new Vector3(80, 20, 0));
45+
var success = button.GetVisual().Properties.TryGetVector3("Translation", out Vector3 translation);
46+
47+
Assert.AreEqual(success, CompositionGetValueStatus.Succeeded);
48+
Assert.AreEqual(translation, new Vector3(80, 20, 0));
49+
50+
string text = VisualExtensions.GetTranslation(button);
51+
52+
Assert.AreEqual(text, new Vector3(80, 20, 0).ToString());
53+
});
54+
}
55+
56+
[TestMethod]
57+
public async Task SetAndAnimateTranslation()
58+
{
59+
await App.DispatcherQueue.EnqueueAsync(async () =>
60+
{
61+
var button = new Button();
62+
var grid = new Grid() { Children = { button } };
63+
64+
VisualExtensions.SetTranslation(button, "80, 20, 0");
65+
66+
await SetTestContentAsync(grid);
67+
68+
await AnimationBuilder.Create()
69+
.Translation(to: new Vector3(11, 22, 0))
70+
.StartAsync(button);
71+
72+
var success = button.GetVisual().Properties.TryGetVector3("Translation", out Vector3 translation);
73+
74+
Assert.AreEqual(success, CompositionGetValueStatus.Succeeded);
75+
Assert.AreEqual(translation, new Vector3(11, 22, 0));
3176

32-
string translation = VisualExtensions.GetTranslation(button);
77+
string text = VisualExtensions.GetTranslation(button);
3378

34-
Assert.AreEqual(translation, new Vector3(80, 20, 0).ToString());
79+
Assert.AreEqual(text, new Vector3(11, 22, 0).ToString());
3580
});
3681
}
3782
}

0 commit comments

Comments
 (0)