Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for OnAppearing of Page called again, although this page was on already replaced NavigationStack #25596

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

BagavathiPerumal
Copy link
Contributor

@BagavathiPerumal BagavathiPerumal commented Oct 30, 2024

Root cause

The issue occurs because the ShellSection.SendAppearing() method triggers PresentedPageAppearing() even when PresentedPage doesn’t match CurrentItem. This mismatch causes the OnAppearing() logic of the previous page to execute at the wrong time, resulting in unintended behavior.

Description of Issue Fix

The fix involves adding the IsPageVisible method to contain the logic for determining if the presented page is the current item. The SendAppearing method was updated to ensure that PresentedPageAppearing is called only if the page is visible, correcting the invocation logic and preventing unintended behavior.

Tested the behavior in the following platforms.

  • Windows
  • Android
  • iOS
  • Mac

Issues Fixed

Fixes #25089

Output

Before Issue Fix After Issue Fix
BeforeFix-OnAppearing.mp4
AfterFix-OnAppearing.mp4

@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Oct 30, 2024
@PureWeen
Copy link
Member

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@jsuarezruiz jsuarezruiz added the area-navigation NavigationPage label Nov 4, 2024
@BagavathiPerumal BagavathiPerumal marked this pull request as ready for review November 5, 2024 08:47
@BagavathiPerumal BagavathiPerumal requested a review from a team as a code owner November 5, 2024 08:47
@jsuarezruiz
Copy link
Contributor

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@jsuarezruiz
Copy link
Contributor

/rebase

@jsuarezruiz
Copy link
Contributor

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

jsuarezruiz
jsuarezruiz previously approved these changes Nov 18, 2024
@rmarinho rmarinho requested a review from PureWeen November 19, 2024 16:32
Copy link
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use a unit test instead of a UITest for this one?

@BagavathiPerumal
Copy link
Contributor Author

Can you use a unit test instead of a UITest for this one?

@PureWeen, we couldn't recreate the same UI test scenario in the unit test case because the Shell.Current is always null when try to navigate to another page by using Shell.Current.GoToAsync().

    [Fact]
    public async Task AppearingFireOnMultipleModals()
    {
        var windowPage = new ContentPage();
        var modalPage1 = new ContentPage();
        var modalPage2 = new ContentPage();

        TestShell newShell = new TestShell
        {
            FlyoutBehavior = FlyoutBehavior.Disabled
        };

        Routing.RegisterRoute("modalPage1", typeof(ContentPage));
        Routing.RegisterRoute("modalPage2", typeof(ContentPage));

        ShellContent homeShellContent = new ShellContent
        {
            FlyoutItemIsVisible = false,
            Route = "windowPage",
            ContentTemplate = new DataTemplate(()=> windowPage)
        };

        newShell.Items.Add(homeShellContent);
        TabBar tabBar = new TabBar();
        Tab testTab = new Tab
        {
            Title = "TestTab"
        };
        
        ShellContent parameterShellContent = new ShellContent
        {
            Route = "modalPage2",
            ContentTemplate = new DataTemplate(()=> modalPage2)
        };

        testTab.Items.Add(parameterShellContent);
        tabBar.Items.Add(testTab);
        newShell.Items.Add(tabBar);

        int modal1Appearing = 0;
        int modal2Appearing = 0;
        int windowAppearing = 0;
        int windowDisappearing = 0;

        modalPage1.Appearing += (_, _) => modal1Appearing++;
        modalPage2.Appearing += (_, _) => modal2Appearing++;
        windowPage.Appearing += (_, _) => windowAppearing++;
        
        var window = new Window(new Shell() { CurrentItem = windowPage });

        await Shell.Current.GoToAsync("modalPage1", true);
        await Shell.Current.GoToAsync("//modalPage2", true);
        await Shell.Current.GoToAsync("//windowPage", true);

        Assert.Equal(1, modal1Appearing);
        Assert.Equal(2, windowAppearing);

    }

@sheiksyedm sheiksyedm added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Dec 6, 2024
@PureWeen PureWeen self-assigned this Jan 5, 2025
Copy link
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the shell instance you instantiated vs Shell.Current

await newShell.GoToAsync("modalPage1", true);
			await newShell.GoToAsync("//modalPage2", true);
			await newShell.GoToAsync("//windowPage", true);

@BagavathiPerumal
Copy link
Contributor Author

Use the shell instance you instantiated vs Shell.Current

await newShell.GoToAsync("modalPage1", true);
			await newShell.GoToAsync("//modalPage2", true);
			await newShell.GoToAsync("//windowPage", true);

@PureWeen, We are unable to recreate the same UI test scenario in the unit test case because the Appearing event is not triggered when navigating from one ShellContent page to another. As a result, we cannot maintain the appearing count in the test case.

[Fact]
public async Task AppearingFireOnMultipleModal()
{
    var windowPage = new MainPage();
    var modalPage1 = new FirstPage();
    TestShell newShell = new TestShell
    {
        FlyoutBehavior = FlyoutBehavior.Disabled
    };
 
    Routing.RegisterRoute("windowPage", typeof(MainPage));
    Routing.RegisterRoute("modalPage1", typeof(FirstPage));
 
    ShellContent shellContent1 = new ShellContent
    {
        FlyoutItemIsVisible = false,
        Route = "windowPage",
        ContentTemplate = new DataTemplate(() => windowPage)
    };
    ShellContent shellContent2 = new ShellContent
    {
        FlyoutItemIsVisible = false,
        Route = "modalPage1",
        ContentTemplate = new DataTemplate(() => modalPage1)
    };
 
    newShell.Items.Add(shellContent1);
    newShell.Items.Add(shellContent2);
 
    modalPage1.Appearing += ModalPage1Appearing;
    windowPage.Appearing += ModalPage2Appearing;
 
    await newShell.GoToAsync("modalPage1", true);
    await newShell.GoToAsync("//windowPage", true);
}
 
private void ModalPage1Appearing(object sender, EventArgs e)
{
}
 
private void ModalPage2Appearing(object sender, EventArgs e)
{
}

@PureWeen
Copy link
Member

PureWeen commented Jan 8, 2025

Use the shell instance you instantiated vs Shell.Current

await newShell.GoToAsync("modalPage1", true);
			await newShell.GoToAsync("//modalPage2", true);
			await newShell.GoToAsync("//windowPage", true);

@PureWeen, We are unable to recreate the same UI test scenario in the unit test case because the Appearing event is not triggered when navigating from one ShellContent page to another. As a result, we cannot maintain the appearing count in the test case.

[Fact]
public async Task AppearingFireOnMultipleModal()
{
    var windowPage = new MainPage();
    var modalPage1 = new FirstPage();
    TestShell newShell = new TestShell
    {
        FlyoutBehavior = FlyoutBehavior.Disabled
    };
 
    Routing.RegisterRoute("windowPage", typeof(MainPage));
    Routing.RegisterRoute("modalPage1", typeof(FirstPage));
 
    ShellContent shellContent1 = new ShellContent
    {
        FlyoutItemIsVisible = false,
        Route = "windowPage",
        ContentTemplate = new DataTemplate(() => windowPage)
    };
    ShellContent shellContent2 = new ShellContent
    {
        FlyoutItemIsVisible = false,
        Route = "modalPage1",
        ContentTemplate = new DataTemplate(() => modalPage1)
    };
 
    newShell.Items.Add(shellContent1);
    newShell.Items.Add(shellContent2);
 
    modalPage1.Appearing += ModalPage1Appearing;
    windowPage.Appearing += ModalPage2Appearing;
 
    await newShell.GoToAsync("modalPage1", true);
    await newShell.GoToAsync("//windowPage", true);
}
 
private void ModalPage1Appearing(object sender, EventArgs e)
{
}
 
private void ModalPage2Appearing(object sender, EventArgs e)
{
}

The sample code you have here isn't quite correct.

Having a Route named "modalPage1" and a shell content named "modalPage1" isn't going to operate correctly.

if you look at ShellLifeCycleTests you'll see there are a number of scenarios where we've wired into appearing and it works alright

@BagavathiPerumal
Copy link
Contributor Author

Use the shell instance you instantiated vs Shell.Current

await newShell.GoToAsync("modalPage1", true);
			await newShell.GoToAsync("//modalPage2", true);
			await newShell.GoToAsync("//windowPage", true);

@PureWeen, We are unable to recreate the same UI test scenario in the unit test case because the Appearing event is not triggered when navigating from one ShellContent page to another. As a result, we cannot maintain the appearing count in the test case.

[Fact]
public async Task AppearingFireOnMultipleModal()
{
    var windowPage = new MainPage();
    var modalPage1 = new FirstPage();
    TestShell newShell = new TestShell
    {
        FlyoutBehavior = FlyoutBehavior.Disabled
    };
 
    Routing.RegisterRoute("windowPage", typeof(MainPage));
    Routing.RegisterRoute("modalPage1", typeof(FirstPage));
 
    ShellContent shellContent1 = new ShellContent
    {
        FlyoutItemIsVisible = false,
        Route = "windowPage",
        ContentTemplate = new DataTemplate(() => windowPage)
    };
    ShellContent shellContent2 = new ShellContent
    {
        FlyoutItemIsVisible = false,
        Route = "modalPage1",
        ContentTemplate = new DataTemplate(() => modalPage1)
    };
 
    newShell.Items.Add(shellContent1);
    newShell.Items.Add(shellContent2);
 
    modalPage1.Appearing += ModalPage1Appearing;
    windowPage.Appearing += ModalPage2Appearing;
 
    await newShell.GoToAsync("modalPage1", true);
    await newShell.GoToAsync("//windowPage", true);
}
 
private void ModalPage1Appearing(object sender, EventArgs e)
{
}
 
private void ModalPage2Appearing(object sender, EventArgs e)
{
}

The sample code you have here isn't quite correct.

Having a Route named "modalPage1" and a shell content named "modalPage1" isn't going to operate correctly.

if you look at ShellLifeCycleTests you'll see there are a number of scenarios where we've wired into appearing and it works alright

@PureWeen, I have added the unit test case based on your suggestion for the scenario. Could you please share any further suggestions.



[Fact]
public async Task VerifyPageAppearingSequence2() // Temporary Name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the shell content so if it's created as a template it creates correctly and life cycle events all fire.

I also added this test to demonstrate an issue with the code added for this fixed.

I think the fix for this one is going to need to be moved into PresentedPageAppearing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-navigation NavigationPage community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration
Projects
None yet
Development

Successfully merging this pull request may close these issues.

OnAppearing of Page called again, although this page was on already replaced NavigationStack
4 participants