From 2e5c49d5d34c1b0a0fbfc52e5ab535e88a58de3e Mon Sep 17 00:00:00 2001 From: HarishwaranVijayakumar Date: Tue, 2 Jun 2026 17:37:41 +0530 Subject: [PATCH 1/3] Add fix --- .../src/Animations/PlatformTicker.Windows.cs | 17 +++++++++++++++++ .../net-windows/PublicAPI.Unshipped.txt | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Core/src/Animations/PlatformTicker.Windows.cs b/src/Core/src/Animations/PlatformTicker.Windows.cs index 35dc7ac8e9d0..92625ca13f66 100644 --- a/src/Core/src/Animations/PlatformTicker.Windows.cs +++ b/src/Core/src/Animations/PlatformTicker.Windows.cs @@ -5,15 +5,32 @@ namespace Microsoft.Maui.Animations /// public class PlatformTicker : Ticker { + bool _isRunning; + + /// + public override bool IsRunning => _isRunning; + /// public override void Start() { + if (_isRunning) + { + return; + } + + _isRunning = true; CompositionTarget.Rendering += RenderingFrameEventHandler; } /// public override void Stop() { + if (!_isRunning) + { + return; + } + + _isRunning = false; CompositionTarget.Rendering -= RenderingFrameEventHandler; } diff --git a/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt index f02df3a74f88..efe8ace1e478 100644 --- a/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt +++ b/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt @@ -1,2 +1,3 @@ -#nullable enable +#nullable enable +override Microsoft.Maui.Animations.PlatformTicker.IsRunning.get -> bool override Microsoft.Maui.Platform.MauiPasswordTextBox.OnCreateAutomationPeer() -> Microsoft.UI.Xaml.Automation.Peers.AutomationPeer! From 0a0188fc39f3706530b0987dc34178470fb331af Mon Sep 17 00:00:00 2001 From: HarishwaranVijayakumar Date: Tue, 2 Jun 2026 18:29:55 +0530 Subject: [PATCH 2/3] Add test --- .../Animations/PlatformTickerTests.Windows.cs | 75 +++++++++++++++++++ src/Core/tests/DeviceTests/TestCategory.cs | 4 + 2 files changed, 79 insertions(+) create mode 100644 src/Core/tests/DeviceTests/Animations/PlatformTickerTests.Windows.cs diff --git a/src/Core/tests/DeviceTests/Animations/PlatformTickerTests.Windows.cs b/src/Core/tests/DeviceTests/Animations/PlatformTickerTests.Windows.cs new file mode 100644 index 000000000000..d7227b999eb4 --- /dev/null +++ b/src/Core/tests/DeviceTests/Animations/PlatformTickerTests.Windows.cs @@ -0,0 +1,75 @@ +#if WINDOWS +using System.Threading.Tasks; +using Microsoft.Maui.Animations; +using Xunit; + +namespace Microsoft.Maui.DeviceTests +{ + [Category(TestCategory.Ticker)] + public class PlatformTickerTests : TestBase + { + [Fact] + public async Task IsRunning_ReflectsStartAndStop() + { + await InvokeOnMainThreadAsync(() => + { + var ticker = new PlatformTicker(); + + Assert.False(ticker.IsRunning, "Should be false before Start"); + + ticker.Start(); + Assert.True(ticker.IsRunning, "Should be true after Start"); + + ticker.Stop(); + Assert.False(ticker.IsRunning, "Should be false after Stop"); + }); + } + + [Fact] + public async Task Start_IsIdempotent_NoDuplicateSubscriptions() + { + await InvokeOnMainThreadAsync(async () => + { + var ticker = new PlatformTicker(); + int fireCount = 0; + ticker.Fire = () => fireCount++; + + ticker.Start(); + ticker.Start(); // second call should be a no-op + ticker.Start(); // third call should be a no-op + + // Yield a few composition frames + await Task.Delay(100); + + ticker.Stop(); + var firesAfterStop = fireCount; + + await Task.Delay(50); + + // No additional fires after Stop + Assert.Equal(firesAfterStop, fireCount); + // ~6 expected at 60Hz over 100ms; 3x subscriptions would yield ~18+ + Assert.InRange(firesAfterStop, 1, 20); + }); + } + + [Fact] + public async Task AnimationManager_StartsTickerOnce_AcrossMultipleAdds() + { + await InvokeOnMainThreadAsync(() => + { + var ticker = new PlatformTicker(); + var manager = new AnimationManager(ticker); + + for (int i = 0; i < 5; i++) + manager.Add(new Animation { Duration = 1.0 }); + + Assert.True(ticker.IsRunning); + + ticker.Stop(); + Assert.False(ticker.IsRunning); + }); + } + } +} +#endif diff --git a/src/Core/tests/DeviceTests/TestCategory.cs b/src/Core/tests/DeviceTests/TestCategory.cs index 3c0f2ebdadcc..61143b504394 100644 --- a/src/Core/tests/DeviceTests/TestCategory.cs +++ b/src/Core/tests/DeviceTests/TestCategory.cs @@ -51,5 +51,9 @@ public static class TestCategory public const string View = "View"; public const string WebView = "WebView"; public const string Window = "Window"; + +#if WINDOWS + public const string Ticker = "Ticker"; +#endif } } \ No newline at end of file From d449fc38b26ade544189b8fe9f2024c2b9a30a24 Mon Sep 17 00:00:00 2001 From: HarishwaranVijayakumar Date: Tue, 2 Jun 2026 19:06:44 +0530 Subject: [PATCH 3/3] Modify test --- .../DeviceTests/Animations/PlatformTickerTests.Windows.cs | 4 +--- src/Core/tests/DeviceTests/TestCategory.cs | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Core/tests/DeviceTests/Animations/PlatformTickerTests.Windows.cs b/src/Core/tests/DeviceTests/Animations/PlatformTickerTests.Windows.cs index d7227b999eb4..b4dbd9bf3dce 100644 --- a/src/Core/tests/DeviceTests/Animations/PlatformTickerTests.Windows.cs +++ b/src/Core/tests/DeviceTests/Animations/PlatformTickerTests.Windows.cs @@ -1,11 +1,10 @@ -#if WINDOWS using System.Threading.Tasks; using Microsoft.Maui.Animations; using Xunit; namespace Microsoft.Maui.DeviceTests { - [Category(TestCategory.Ticker)] + [Category(TestCategory.Animation)] public class PlatformTickerTests : TestBase { [Fact] @@ -72,4 +71,3 @@ await InvokeOnMainThreadAsync(() => } } } -#endif diff --git a/src/Core/tests/DeviceTests/TestCategory.cs b/src/Core/tests/DeviceTests/TestCategory.cs index 61143b504394..3b736e46e89c 100644 --- a/src/Core/tests/DeviceTests/TestCategory.cs +++ b/src/Core/tests/DeviceTests/TestCategory.cs @@ -51,9 +51,6 @@ public static class TestCategory public const string View = "View"; public const string WebView = "WebView"; public const string Window = "Window"; - -#if WINDOWS - public const string Ticker = "Ticker"; -#endif + public const string Animation = "Animation"; } } \ No newline at end of file