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; }