Udostępnij za pośrednictwem


Multiple Windows/Views in a Windows 10 XAML Store App

 

Back in Windows 8, Modern Apps were full screen and could only have one window open. In Windows 10, a Universal Application (UWP) can have multiple windows (views) open at once so that a user can, for example, work on multiple documents at once, and switch between windows just like classic Win32 applications such as Word or PowerPoint.

In this post, I’ll show you just how easy it is to do that, and how all UWP apps should implement multiple windows if it is appropriate for their functionality.

All you need to be multi-window is to call the View management APIs in your App.xaml.cs. In your OnLaunched method just add the following code:

 

protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null) // First activation
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}

        Window.Current.Activate();
dispatchers.Add(CoreWindow.GetForCurrentThread().Dispatcher);
}
else // Subsequent activations - create new view/UI thread
{
var view = CoreApplication.CreateNewView();
int windowId = 0;
await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
windowId = ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread());
var frame = new Frame();
frame.Navigate(typeof(MainPage), null);
Window.Current.Content = frame;
Window.Current.Activate();
ApplicationView.GetForCurrentView().Consolidated += View_Consolidated;
});

        // Run this on the last dispatcher so the windows get positioned correctly
bool viewShown;
await dispatchers[dispatchers.Count - 1].RunAsync(CoreDispatcherPriority.Normal, async () =>
{ viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(windowId); });
dispatchers.Add(view.Dispatcher);
}
}

 

Each view gets its own UI thread. Note how I keep a list of all the dispatchers as this can be useful if you need to do operations in the other windows. It is really important to avoid cross thread access of UI resources as this will crash your application with a cross-thread exception. The solution I use is to have completely separate ViewModel’s for each thread, that are easily accessed in code with ViewModel.Current to reference the ViewModel for the current thread.

 

public class ViewModel
{
private static Dictionary<CoreDispatcher, ViewModel> viewModels = new Dictionary<CoreDispatcher, ViewModel>();
private static int count = 0;
public static ViewModel Current
{
get
{
lock (viewModels)
{
var dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
if (dispatcher == null)
throw new ArgumentException("ViewModel.Current must be called from a UI thread");
ViewModel viewModel = null;
if (viewModels.TryGetValue(dispatcher, out viewModel))
return viewModel;
viewModel = new ViewModel();
viewModels[dispatcher] = viewModel;
viewModel.InstanceId = ++count; // This is just for the demo project
return viewModel;
}
}
}

    public int InstanceId { get; set; }
}

 

Calling ViewModel.Current always returns the correct ViewModel corresponding to the View’s UI thread, so it is very difficult to run into cross-thread problems.

 

Here is a test project with all the code. Run it, then right click the taskbar icon and run more instances. You will see that each view gets its own ViewModel and displays different content.

 

image

 

The test project can be downloaded here: You are free to use it in your code, even for commercial use.

 

https://theuxblog20160710035436.azurewebsites.net/MultiWindowSample.zip

 

Enjoy!

Paul Tallett, UX Global Practice, Microsoft UK

Disclaimer: The information on this site is provided "AS IS" with no warranties, confers no rights, and is not supported by the authors or Microsoft Corporation. Use of included script samples are subject to the terms specified in the Terms of Use.