diff --git a/src/Controls/src/Core/VisualElement/VisualElement.cs b/src/Controls/src/Core/VisualElement/VisualElement.cs index 70c46aa8b1dc..c14f2c632ceb 100644 --- a/src/Controls/src/Core/VisualElement/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement/VisualElement.cs @@ -2139,17 +2139,26 @@ public bool IsLoaded } /// - /// Occurs when an element has been constructed and added to the object tree. + /// Occurs when an element has been constructed and added to the platform visual tree. /// /// This event may occur before the element has been measured so should not be relied on for size information. public event EventHandler? Loaded { add { + bool watchingLoaded = _watchingPlatformLoaded; _loaded += value; UpdatePlatformUnloadedLoadedWiring(Window); - if (_isLoadedFired) - _loaded?.Invoke(this, EventArgs.Empty); + + // The point of this code, is to fire loaded if the element is already loaded. + // + // If this is the first time the user is subscribing to Loaded, + // UpdatePlatformUnloadedLoadedWiring will take care of firing Loaded. + // If we are already wired up to watch loaded, then we'll fire it off if we know this + // view is in a state where it's been determined that it's accurate to fire + // _isLoadedFired. + if (_isLoadedFired && watchingLoaded) + value?.Invoke(this, EventArgs.Empty); } remove @@ -2160,7 +2169,7 @@ public event EventHandler? Loaded } /// - /// Occurs when an element is no longer connected to the main object tree. + /// Occurs when an element is no longer connected to the platform visual tree. /// public event EventHandler? Unloaded { diff --git a/src/Controls/tests/Core.UnitTests/PageLifeCycleTests.cs b/src/Controls/tests/Core.UnitTests/PageLifeCycleTests.cs index d41ff70c2072..7211de857b1b 100644 --- a/src/Controls/tests/Core.UnitTests/PageLifeCycleTests.cs +++ b/src/Controls/tests/Core.UnitTests/PageLifeCycleTests.cs @@ -262,6 +262,71 @@ public async Task PushSecondModalPage() Assert.Equal(1, firstModalPage.AppearingCount); } + [Fact] + public async Task LoadedUnLoadedEvents() + { + var previousPage = new LCPage(); + var lcPage = new LCPage(); + var navigationPage = + new TestNavigationPage(true, previousPage) + .AddToTestWindow(); + + await navigationPage.PushAsync(lcPage); + + int loadedCnt = 0; + int unLoadedCnt = 0; + lcPage.Loaded += (_, _) => loadedCnt++; + lcPage.Unloaded += (_, _) => unLoadedCnt++; + + Assert.Equal(1, loadedCnt); + Assert.Equal(0, unLoadedCnt); + + await navigationPage.PopAsync(); + + Assert.Equal(1, loadedCnt); + Assert.Equal(1, unLoadedCnt); + } + + [Fact] + public async Task LoadedFiresOnInitialSubscription() + { + var previousPage = new LCPage(); + var lcPage = new LCPage(); + var navigationPage = + new TestNavigationPage(true, previousPage) + .AddToTestWindow(); + + await navigationPage.PushAsync(lcPage); + + int loadedCnt = 0; + int unLoadedCnt = 0; + + Assert.True(lcPage.IsLoaded); + + // Wire up to loaded event to setup wiring + lcPage.Loaded += (_, _) => + { + loadedCnt++; + }; + + // Subscribing to loaded should fire the loaded + // event if the page is already loaded + lcPage.Loaded += (_, _) => + { + loadedCnt++; + }; + + lcPage.Unloaded += (_, _) => unLoadedCnt++; + + Assert.Equal(2, loadedCnt); + Assert.Equal(0, unLoadedCnt); + + await navigationPage.PopAsync(); + + Assert.Equal(2, loadedCnt); + Assert.Equal(1, unLoadedCnt); + } + public class LCPage : ContentPage { public NavigatedFromEventArgs NavigatedFromArgs { get; private set; }