Multiple Windows with Windows 10 and JavaScript

So a little known fact of Universal Windows Programs (UWPs), is that they can have more than a single window. At least it was little know to me until I was asked by a partner if there was a way to do it. As it turns out, there’s actually two options. There’s the multiple window approach used by applications like the Mail app. There’s also the projection manager approach where you intentionally want to display a window on a seperate screen from the main window. These approaches have actually been around since Windows 8 and center on the use of the ApplicationView object.

In poking around, I found that there’s a solid collection of Windows 8.1 and Visual Studio 2013 samples for C++, C#, and even JavaScript. There was even an excellent blog post about implementing multiple windows/views with Javascript, but it was again focused on Windows 8.1. But I needed an example of this approach with Windows 10 and Visual Studio 2015. And unfortunately, the Windows 8 example wasn’t working. So I set about fixing it.

WinJS 4 and the missing MSAppView

It turns out when moving from Windows 8 to Windows 10, you’re also moving from WinJS 2 to WinJS 4. In WinJS 4, some of the more “Microsoft-ish” ways of doing things was dropped in favor of allowing JavaScript developers to leverage the way they’ve always done things. The primary change was the deprecation of the MSAppView object and its various functions. This object represented a Windows Store app’s window (app view) and gave us methods like close and postMessage as well as a property that was the viewId of the window.

With this object removed, some of the functionality of the afformentioned Windows 8.1 sample was broken. So to get it working, I had to find a replacement, or better yet, build one.

myAppView

I started by crafting a replacement for the deprecated MSAppView which I called simply enough myAppView. Giving this object some of the sames functions/properties as its predecessor.

    function myAppView(window) {
        this.viewId = MSApp.getViewId(window),
        this.window = window,
        this.postMessage = function (message, domain) {
            this.window.postMessage(message, domain);
        }
    };

With this in place, I also needed to implement two more missing fuctions, the MSApp.createNewView (the WinJS implementation of CoreApplication.CreateNewView). Since these would be “global” functions, I opted to wrap them in a WinJS namespace called CustomAppView. Here’s the createNewView implementation.

    createNewView: function(page) {
        var newWindow = window.open(page, null, "msHideView=yes");
        //var newWindow = window.open(page);

        return new myAppView(newWindow);
    }

This would instantiate a new window using the common JavaScript function window.open. However, what we’ve done is add the value myHideView=yes in the optional “replace” parameter. This directive means the window exists, but isn’t yet visible. I searched the internet and for the life of me couldn’t find a single referrence for this. Thankfully, I was able to track someone down inside of the team responsible for WinJS and they shared this little gem with me. Then using the handle for this new and invisible window, I create an instance of the myAppView object. This object exposes the same functions/properties as its predicesor, allowing much of the sample code to remain in place.

We also had to craft a replacement for MSApp.getViewOpener:

    getViewOpener: function () {
        var openerWindow = window.opener;
        var parentWindow = window.parent;
        return new myAppView(parentWindow);
    }

You can see the full implementation of the myAppView and the two functions on GitHub.

 Out with the old, in with the new

In the existing sample, I was focused on getting “Scenario 1” working. This centered around the ViewManager object and functions for managing the view. The first of those was one to create a new view from a URL. In here, I had to replace the existing MSApp.createNewView with my new implementation.

    //var newView = MSApp.createNewView(page);
    //BMS
    var newView = CustomAppView.createNewView(page);

So we call the method we crafted above to create the window and return a copy of the myAppView object.

Next up, I had to replace MSApp.getViewOpener, which will be used to help me keep track of who “owns” the current window to assist in sending message between the windows.

    //this.opener = MSApp.getViewOpener();
    //BMS
    this.opener = CustomAppView.getViewOpener();

I played a bit to see which works better, the window.opener and window.parent, and found that parent gave me the best results. However, its important to note that according to the official documentation, this could return a window that “may not necessarily be the same window reported by the window.parent or window.opener properties”. But I found one that worked for my needs, so hopefully it will work for you also.

I’m still no expert

I’d like to stress, I know just enough JavaScript to continue to find it frustrating. The challenges I have with it are well known by my colleagues. However, I do appreciate opportunities like this that force me to dig into it and learn more. And this is one of those times where I hope my frustration will help save you some. I’ve posted the complete project up on GitHub so you can pull and try yourself. Just be forewarned that I’ve only really focused on Scenario 1, so the others may still have issues. Smile I’ll try to keep working on this from time to time to make the sample a bit better (it really needs some inline comments).

Until next time!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: