Search code examples
c#xamarinxamarin.uwpuwp-appwindow

Xamarin UWP - navigation issue after opening standalone Appwidow


I create an application in which I use multiple standalone window. But I have an issue with navigation after opening a standalone Appwindow. To open a standalone window I created an interface in Core project 'library class' solution and a Dependency service in UWP. The interface code:

public interface IOpenWindow
{
    Task OpenSecondWindow();
}

The code for the dependency

public class OpenWindow : IOpenWindow
{
    private AppWindow appWindow;
    private Frame appWindowFrame = new Frame();
    public async Task OpenSecondWindow()
    {
        // Only ever create one window. If the AppWindow already exists call TryShow on it to bring it to foreground.
        if (appWindow == null)
        {
            // Create a new window
            appWindow = await AppWindow.TryCreateAsync();
            // Make sure we release the reference to this window, and release XAML resources, when it's closed
            appWindow.Closed += delegate { appWindow = null; appWindowFrame.Content = null; };

            //Navigate the frame to the page we want to show in the new window
            appWindowFrame.Navigate(typeof(TheStandalonePage)); // I created a page different than MainPage in UWP project

            // Attach the XAML content to our window
            ElementCompositionPreview.SetAppWindowContent(appWindow, appWindowFrame);
        }

        // Now show the window
        await appWindow.TryShowAsync();
    }
}

Then in the Xaml page 'TheStandalonePage' in UWP project, I load the application again as follow:

    public TheStandalonePage()
    {
        this.InitializeComponent();
        //This is a boolean used to select in 'COREAPP.App.' the correct MainPage to show
        COREAPP.App.OpenStandAlonePage = true;
        LoadApplication(new COREAPP.App());
        MyDependencies.RegisterAll();
    }

Then in the COREAPP I made sure to specify how to open the correct page choosed with the interface as follow:

    public static bool OpenStandAlonePage { get; set; } = false;
    public App()
    {
        InitializeComponent();

        if (OpenStandAlonePage)
        {
            MainPage = new MyStandAlonePage();
        }
        else
        {
            MainPage = new MainPage();
        }
    }

After 'MyStandAlonePage' took over the MainPage while opening in a new window, it is impossible for me to use App.Current.Mainpage = new AnotherPage(); in MainPage()

Do someone know if there is a possibity make it happens in any way? Any help will be appreciated 🙌🏿


Solution

  • I found the solution to my issue. I will try to explain what I understood. Feel free to correct me if I am wrong.

    1. Xamarin.Forms 'Mainpage = new SpecificPage()' works only on the Window.Current.Active() functionnality when you are using UWP. Which means that, if you open a standalone window (AppWindow), changing the 'Mainpage' will be effective on this standalone window.
    2. If you which to apply a change of the 'Mainpage' using Xamarin.Forms, you will need to change Window.Current.Active() inside UWP project.

    This is how I solve the issue.

    First, I updated the interface used for standalone page like below. You can find 'Task OpenSecondWindow()' in the original question

    public interface IOpenWindow
    {
        Task OpenSecondWindow(); // Used to open a standalone window
        void ResetToMainWindow(); // Used the reset the current window to the mainpage
    }
    

    Second, I created the related function inside the dependency injection class in UWP project

    public class OpenWindow : IOpenWindow
    {
        private AppWindow appWindow;
        private Frame appWindowFrame = new Frame();
    
        public void ResetToMainWindow()
        {
            // You can find the same functionnalities in the 'App' class in UWP project
            Frame rootFrame = Window.Current.Content as Frame;
            if (rootFrame != null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();
                rootFrame.NavigationFailed += OnNavigationFailed;
    
                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
    
                // If we're currently not on a page, navigate to the main page
                if (rootFrame.Content == null)
                {
                    COREAPP.App.OpenMainPage = true;
                    rootFrame.Navigate(typeof(MainPage));
                }
    
                // Ensure the current window is active
                Window.Current.Activate();
            }
        }
    
        private void OnNavigationFailed(object sender, Windows.UI.Xaml.Navigation.NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }
    }
    

    WARNING - The functionnality above will simulate the initial launch of the application by using rootFrame.Navigate(typeof(MainPage));.

    Because it will navigate to MainPage in your UWP project where you first launch the application through the constructor.

    If you do not want your application to execute the the first launch process again, you need to respect a few conditions:

    First condition:

    you must add in 'public App()' in the core project a new boolean 'SecondaryWindowHasBeenOpen' which will helps you to verify if a standalone page is opened. You also need a boolean 'OpenMainPage' to instruct the application not to open a new standalone page of your 'MainPage' (MainPage = new MainPage).

        public static bool OpenStandAlonePage { get; set; } = false;
        public static bool SecondaryWindowHasBeenOpen { get; set; } = false;
        public static bool OpenMainPage { get; set; } = false;
        public App()
        {
            InitializeComponent();
            
            if (OpenStandAlonePage || SecondaryWindowHasBeenOpen)
            {
                if (!OpenMainPage)
                {
                    // This part ensure the creation of a new standalone page
                    MainPage = new MyStandAlonePage();
                }
                else
                {
                    //This part ensure to not create a new standalone page
                    //It will make sure to not use the process for first app launch...
                    //and at the same time, will ensure your app to proceed with...
                    //the modification of 'MainPage' in your code in execution
                    OpenMainPage = false;
                }
            }
            else
            {
                MainPage = new MainPage();
            } 
        }
    

    Second condition: You need to mark the boolean 'SecondaryWindowHasBeenOpen' as true in the constructor of the standalone page in UWP project.

        public TheStandalonePage()
        {
            this.InitializeComponent();
            //This is a boolean used to select in 'COREAPP.App.' the correct MainPage to show
            COREAPP.App.OpenStandAlonePage = true;
            
            //This will ensure that in COREAPP.App...
            //when you want to proceed to the modification of MainPage...
            //it will not load the application as a first launch
            COREAPP.App.SecondaryWindowHasBeenOpen = true;
            
            LoadApplication(new COREAPP.App());
            MyDependencies.RegisterAll();
        }
    

    Third condition: This condition is already fullfil in the dependency injection code above where you can find 'COREAPP.App.OpenMainPage = true;'

    Last and most important condition: wherever in your application when you want to execute the modification of the Mainpage in your main window, apply the following:

    1. Create a static class and the function to call in the CORE project

      public static class MainWindow
      {
          public static void Activate()
          {
              if (App.SecondaryWindowHasBeenOpen)
                  DependencyService.Get<IOpenWindow>().ResetToMainWindow();
          }
      }
      
    2. Call the static function wherever is needed MainWindow.Activate();

    This is how I solve the issue.