UWP problems with multiple views
Asked Answered
I

4

7

I am writing an app which should be able to run multiple views to edit different documents each in their own window. I've wrote some code which works, but I'm having some problems with it. The code I wrote is based upon the Multiple Views sample provided by Microsoft (https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/MultipleViews).

I have mainly two problems. The first one is if I close the main view, that is the first window that was open when the application was launched, then I cannot open any new views/windows by clicking in the app tile or opening an associated file type, until I close all views/windows and relaunch the app. The second one, is that when I try to open a new view/window from MainPage.xaml.cs, the app just crashes.

The code that I use to manage the views in App.xaml.cs is the following:

sealed partial class App : Application
{
    //I use this boolean to determine if the application has already been launched once
    private bool alreadyLaunched = false;

    public ObservableCollection<ViewLifetimeControl> SecondaryViews = new ObservableCollection<ViewLifetimeControl>();
    private CoreDispatcher mainDispatcher;
    public CoreDispatcher MainDispatcher
    {
        get
        {
            return mainDispatcher;
        }
    }

    private int mainViewId;
    public int MainViewId
    {
        get
        {
            return mainViewId;
        }
    }

    public App()
    {
        this.InitializeComponent();
        this.Suspending += OnSuspending;
    }

    protected override async void OnLaunched(LaunchActivatedEventArgs e)
    {
        Frame rootFrame = Window.Current.Content as Frame;

        if (rootFrame == null)
        {
            rootFrame = new Frame();

            rootFrame.NavigationFailed += OnNavigationFailed;

            if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                //TODO: Load state from previously suspended application
            }

            // Place the frame in the current Window
            Window.Current.Content = rootFrame;
        }

        if (rootFrame.Content == null)
        {
            alreadyLaunched = true;
            rootFrame.Navigate(typeof(MainPage), e.Arguments);
        }
        else if(alreadyLaunched)
        {
            var selectedView = await createMainPageAsync();
            if (null != selectedView)
            {
                selectedView.StartViewInUse();
                var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
                    selectedView.Id,
                    ViewSizePreference.Default,
                    ApplicationView.GetForCurrentView().Id,
                    ViewSizePreference.Default
                    );

                await selectedView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
                    Window.Current.Activate();
                });

                selectedView.StopViewInUse();
            }
        }
        // Ensure the current window is active
        Window.Current.Activate();
    }

    protected override async void OnFileActivated(FileActivatedEventArgs args)
    {
        base.OnFileActivated(args);

        if (alreadyLaunched)
        {
            //Frame rootFrame = Window.Current.Content as Frame;
            //((MainPage)rootFrame.Content).OpenFileActivated(args);
            var selectedView = await createMainPageAsync();
            if (null != selectedView)
            {
                selectedView.StartViewInUse();
                var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
                    selectedView.Id,
                    ViewSizePreference.Default,
                    ApplicationView.GetForCurrentView().Id,
                    ViewSizePreference.Default
                    );

                await selectedView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
                    Window.Current.Activate();
                    currentPage.OpenFileActivated(args);
                });

                selectedView.StopViewInUse();
            }
        }
        else
        {
            Frame rootFrame = new Frame();
            rootFrame.Navigate(typeof(MainPage), args);
            Window.Current.Content = rootFrame;
            Window.Current.Activate();
            alreadyLaunched = true;
        }
    }

    partial void Construct();
    partial void OverrideOnLaunched(LaunchActivatedEventArgs args, ref bool handled);
    partial void InitializeRootFrame(Frame frame);

    partial void OverrideOnLaunched(LaunchActivatedEventArgs args, ref bool handled)
    {
        // Check if a secondary view is supposed to be shown
        ViewLifetimeControl ViewLifetimeControl;
        handled = TryFindViewLifetimeControlForViewId(args.CurrentlyShownApplicationViewId, out ViewLifetimeControl);
        if (handled)
        {
            var task = ViewLifetimeControl.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                Window.Current.Activate();
            });
        }
    }

    partial void InitializeRootFrame(Frame frame)
    {
        mainDispatcher = Window.Current.Dispatcher;
        mainViewId = ApplicationView.GetForCurrentView().Id;
    }

    bool TryFindViewLifetimeControlForViewId(int viewId, out ViewLifetimeControl foundData)
    {
        foreach (var ViewLifetimeControl in SecondaryViews)
        {
            if (ViewLifetimeControl.Id == viewId)
            {
                foundData = ViewLifetimeControl;
                return true;
            }
        }
        foundData = null;
        return false;
    }

    private async Task<ViewLifetimeControl> createMainPageAsync()
    {
        ViewLifetimeControl viewControl = null;
        await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            // This object is used to keep track of the views and important
            // details about the contents of those views across threads
            // In your app, you would probably want to track information
            // like the open document or page inside that window
            viewControl = ViewLifetimeControl.CreateForCurrentView();
            viewControl.Title = DateTime.Now.ToString();
            // Increment the ref count because we just created the view and we have a reference to it                
            viewControl.StartViewInUse();

            var frame = new Frame();
            frame.Navigate(typeof(MainPage), viewControl);
            Window.Current.Content = frame;
            // This is a change from 8.1: In order for the view to be displayed later it needs to be activated.
            Window.Current.Activate();
            //ApplicationView.GetForCurrentView().Title = viewControl.Title;
        });

        ((App)App.Current).SecondaryViews.Add(viewControl);

        return viewControl;
    }

    void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
    {
        throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
    }

    private void OnSuspending(object sender, SuspendingEventArgs e)
    {
        var deferral = e.SuspendingOperation.GetDeferral();
        //TODO: Save application state and stop any background activity
        deferral.Complete();
    }

    //I call this function from MainPage.xaml.cs to try to open a new window
    public async void LoadNewView()
    {
        var selectedView = await createMainPageAsync();
        if (null != selectedView)
        {
            selectedView.StartViewInUse();
            var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
                selectedView.Id,
                ViewSizePreference.Default,
                ApplicationView.GetForCurrentView().Id,
                ViewSizePreference.Default
                );

            await selectedView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
                Window.Current.Activate();
                currentPage.LoadNewFile();
            });

            selectedView.StopViewInUse();
        }
    }
}

The code I use to try to launch a new view/window from MainPage.xaml.cs:

((App)App.Current).LoadNewView();

I've been reading the Microsoft documentation to try and understand what is the issue, but I still don't understand how exactly do Multiple Views work, like if the App class instantiated every time I open a new view/window.

I'd really appreciate the help.

Involutional answered 17/4, 2016 at 19:53 Comment(3)
This code currentPage.LoadNewFile(); is invoking the Mainpage's LoadNewFile. Did you write another LoadNewFile() method in your Mainpage.xaml.cs? Why you put all the code in App.xaml.cs? Better to upload all your code.Allegra
Yes, I did write the LoadNewFile() method in my MainPage.xaml.cs, and I put all the code for multiple views in App.xaml.cs so I don't have to rewrite code, but I did try to create the new view/window from my MainPage.xaml.cs with the same errors. The app crashes when CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => runs in the createMainPageAsync() method, and the strangest thing is that it tries to open another debugger instead of Visual StudioCupola
In that case, upload all your code, otherwise people cannot help you without the error code ,error details.Allegra
I
3

I've found the solution to my problems, and I've actually decided not to use the ViewLifeTime control that comes with the sample.

The problem is that when the Main view is closed you have to use the Dispatcher.RunAsync() method from one of the other views that are still open to run it that thread

Here's the code that I've changed in my App.xaml.cs for anyone that is interested:

public bool isMainViewClosed = false;
public ObservableCollection<CoreApplicationView> secondaryViews = new ObservableCollection<CoreApplicationView>();

//...

protected override async void OnLaunched(LaunchActivatedEventArgs e)
    {
        Frame rootFrame = Window.Current.Content as Frame;

        if (rootFrame == null)
        {
            rootFrame = new Frame();

            rootFrame.NavigationFailed += OnNavigationFailed;

            if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                //TODO: Load state from previously suspended application
            }
            Window.Current.Content = rootFrame;
        }

        if (rootFrame.Content == null)
        {
            alreadyLaunched = true;
            rootFrame.Navigate(typeof(MainPage), e.Arguments);
        }
        else if(alreadyLaunched)
        {
    //If the main view is closed, use the thread of one of the views that are still open
            if(isMainViewClosed)
            {
                int newViewId = 0;
                await secondaryViews[0].Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
                    Window.Current.Activate();
                    currentPage.NewWindow();
                    newViewId = ApplicationView.GetForCurrentView().Id;
                });
                bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
            }
            else
            {
                CoreApplicationView newView = CoreApplication.CreateNewView();
                int newViewId = 0;
                await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    Frame frame = new Frame();
                    frame.Navigate(typeof(MainPage), null);
                    Window.Current.Content = frame;
                    var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
                    Window.Current.Activate();

                    secondaryViews.Add(CoreApplication.GetCurrentView());
                    newViewId = ApplicationView.GetForCurrentView().Id;
                });
                bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
            }
        }
        Window.Current.Activate();
    }
Involutional answered 5/5, 2016 at 17:13 Comment(0)
S
8

Actually the proper way to still be able to open up new windows after the main one is closed is to use one of the overloads provided by TryShowAsStandaloneAsync.

protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
    // Create the newWindowId and stuff...

    await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newWindowId, 
        ViewSizePreference.Default,
        e.CurrentlyShownApplicationViewId, 
        ViewSizePreference.Default);

Basically, you need to specify the third parameter anchorViewId which is

the ID of the calling (anchor) window.

In this case, you just need to pass in e.CurrentlyShownApplicationViewId.

Scanty answered 5/10, 2016 at 20:49 Comment(0)
I
3

I've found the solution to my problems, and I've actually decided not to use the ViewLifeTime control that comes with the sample.

The problem is that when the Main view is closed you have to use the Dispatcher.RunAsync() method from one of the other views that are still open to run it that thread

Here's the code that I've changed in my App.xaml.cs for anyone that is interested:

public bool isMainViewClosed = false;
public ObservableCollection<CoreApplicationView> secondaryViews = new ObservableCollection<CoreApplicationView>();

//...

protected override async void OnLaunched(LaunchActivatedEventArgs e)
    {
        Frame rootFrame = Window.Current.Content as Frame;

        if (rootFrame == null)
        {
            rootFrame = new Frame();

            rootFrame.NavigationFailed += OnNavigationFailed;

            if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                //TODO: Load state from previously suspended application
            }
            Window.Current.Content = rootFrame;
        }

        if (rootFrame.Content == null)
        {
            alreadyLaunched = true;
            rootFrame.Navigate(typeof(MainPage), e.Arguments);
        }
        else if(alreadyLaunched)
        {
    //If the main view is closed, use the thread of one of the views that are still open
            if(isMainViewClosed)
            {
                int newViewId = 0;
                await secondaryViews[0].Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
                    Window.Current.Activate();
                    currentPage.NewWindow();
                    newViewId = ApplicationView.GetForCurrentView().Id;
                });
                bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
            }
            else
            {
                CoreApplicationView newView = CoreApplication.CreateNewView();
                int newViewId = 0;
                await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    Frame frame = new Frame();
                    frame.Navigate(typeof(MainPage), null);
                    Window.Current.Content = frame;
                    var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
                    Window.Current.Activate();

                    secondaryViews.Add(CoreApplication.GetCurrentView());
                    newViewId = ApplicationView.GetForCurrentView().Id;
                });
                bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
            }
        }
        Window.Current.Activate();
    }
Involutional answered 5/5, 2016 at 17:13 Comment(0)
T
0

Optionally you can use multiple instances for your application. You can synchronize settings changes as I have described here.

Tegucigalpa answered 8/7, 2020 at 10:43 Comment(0)
K
-1

don't View(your)Lifetime away ... cheers,

int idCreate = 0; List<int> idSaved = new List<int>();
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;
    if (rootFrame == null)
    {
        rootFrame = new Frame();
        rootFrame.NavigationFailed += OnNavigationFailed;
        Window.Current.Content = rootFrame;
    }

    if (rootFrame.Content == null)
    {
        rootFrame.Navigate(typeof(MainPage), e.Arguments);
        idSaved.Add(ApplicationView.GetForCurrentView().Id);
    }
    else
    {
        var create = CoreApplication.CreateNewView(); 
        await create.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            var frame = new Frame();
            frame.Navigate(typeof(MainPage), e.Arguments);
            Window.Current.Content = frame;
            Window.Current.Activate();

            idCreate = ApplicationView.GetForCurrentView().Id;
        });

        for(int i = idSaved.Count - 1; i >= 0; i--)
            if (await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
                    idCreate, ViewSizePreference.UseMinimum, 
                    idSaved[i], ViewSizePreference.UseMinimum)
               ) break;

        idSaved.Add(idCreate);
    }
    Window.Current.Activate();
}
Kobayashi answered 6/8, 2016 at 2:4 Comment(2)
If you wish to delete your answer, simply click the delete button.Linnet
@FrankerZ, unregistered users cannot delete their answers.Alburnum

© 2022 - 2024 — McMap. All rights reserved.