It’s a common need to have some commands on an app’s settings panel that are web links (and thus open in a browser, often used for one’s privacy policy) and others that are flyouts (and thus open in a WinJS.UI.Flyout control). However, it’s a little tricky to mix the two, as the method is specifically designed to work with settings panes defined in separate .html files, and not with links. I hinted at how to do this in Chapter 8 of my book, but didn’t spell it out. Let me remedy that in this post.

First, the event that comes form WinRT when the settings charm is invoked is Windows.UI.ApplicationSettings.commandrequested, which you listen to as follows:

var as = Windows.UI.ApplicationSettings;
var settingsPane = as.SettingsPane.getForCurrentView();
settingsPane.addEventListener(“commandsrequested”, onCommandsRequested);

To create a link command in the settings pane, you do the following:

function onCommandsRequested(e) {
    var commandHelp = new as.SettingsCommand(“privacy”, “Privacy Policy”, privacyCommandInvoked);
    e.request.applicationCommands.append(commandHelp);
}

function privacyCommandInvoked(e) {
    var uri = new Windows.Foundation.Uri(http://www.kraigbrockschmidt.com/src/HereMyAm_PrivacyStatement.html);
    Windows.System.Launcher.launchUriAsync(uri).done();
}

(The URI here is the privacy policy for the Here My Am! app in the book.)

To create WinJS flyout commands, you instead use WinJS.Application.onsettings, typically with WinJS.UI.SettingsFlyout.populateSettings to create settings commands that open flyouts,  as shown in the App settings sample, scenario 2:

WinJS.Application.onsettings = function (e) {
    e.detail.applicationcommands = { “help”: { title: “Help”, href: “/html/2-SettingsFlyout-Help.html” } };
    WinJS.UI.SettingsFlyout.populateSettings(e);
};

However, it doesn’t work to insert a link command in this way (e.g. putting a URI in the href field), because populateSettings assumes that the href points to a local file, which it will attempt to load when that command is invoked. If you put http:// URI in the href, you’ll get an exception.

So how do you mix the two?

There are two key things to know:

  1. Within WinJS.Application.onsettings, the e.detail.e is the same as the event args that come into the WinRT oncommandsrequested event.
  2. WinJS.UI.SettingsFlyout.populateCommands simply goes through whatever is in e.detail.applicationcommands (an object) and creates settings commands that open the flyout pane. It does this with e.detail.e.request.applicationCommands.append(). (Take a look at the source code in ui.js of WinJS to see all it does.)

What this means is that within WinJS.Application.onsettings you can also just create Windows.UI.ApplicationSettings.SettingsCommand objects directly, and pass them to the append method or insertAt. So if I wanted to add a Privacy Policy link after the WinJS flyout Help setting, I can do this (making a modification to scenario 2 of the sample):

function scenario2AddSettingsFlyout() {
    WinJS.Application.onsettings = function (e) { 
        e.detail.applicationcommands = { “help”: { title: “Help”, href: “/html/2-SettingsFlyout-Help.html” }, };
        WinJS.UI.SettingsFlyout.populateSettings(e);

        var as = Windows.UI.ApplicationSettings;
        var command = new as.SettingsCommand(“privacy”, “Privacy Policy”, privacyCommandInvoked); 
        e.detail.e.request.applicationCommands.append(command);
    };

    function privacyCommandInvoked(e) {
        var uri = new Windows.Foundation.Uri(“http://www.kraigbrockschmidt.com/src/HereMyAm_PrivacyStatement.html”);
        Windows.System.Launcher.launchUriAsync(uri).done();
    }

    // …
}
You can, of course, create the link settings first, then create the flyouts. Ultimately everything resolves to call to append so you can do it in either order. You can use insertAt to place commands directly if you want.

UPDATE: my previous statement about interspersing flyouts and commands by calling populateSettings multiple times with different contents in e.detail.applicationcommands doesn’t actually work. The problem is that the call to populateSettings stores the applicationCommands object you pass in another variable, and it uses this variable in its handler for the command to check if the command is valid. So although command from earlier calls to populateSettings appear in the Settings pane, they won’t do anything because WinJS won’t find that command.

To correct this, you basically need to create your own simple handler to call WinJS.UI.SettingsFlyout.showSettings, which is all that populateSettings does. As an example, here’s an update to the code above that borrows a flyout from scenario 3 of the sample (I’ve added two lines to the end of the onsettings function and the legalNoticesCommandInvoked function):

    WinJS.Application.onsettings = function (e) { 
        e.detail.applicationcommands = { “help”: { title: “Help”, href: “/html/2-SettingsFlyout-Help.html” },
};
        WinJS.UI.SettingsFlyout.populateSettings(e);

        var as = Windows.UI.ApplicationSettings;
        var command = new as.SettingsCommand(“privacy”, “Privacy Policy”
, privacyCommandInvoked); 
        e.detail.e.request.applicationCommands.append(command);

        var command = new as.SettingsCommand(“legalNotices”, “Legal Notices”, legalNoticesCommandInvoked); 
        e.detail.e.request.applicationCommands.append(command);

    };
    function legalNoticesCommandInvoked(e) {
        WinJS.UI.SettingsFlyout.showSettings(“legalNotices”, “/html/3-SettingsFlyout-Legal.html”);
    }

So with this, use populateSettings for the largest group of flyouts that you have, then append or insert individual commands with your own handlers for additional flyouts. Alternately, just go to WinJS’s ui.js file, copy the populateSettings code, and make your own version that keeps a full list of commands that your own generic handler uses for lookup. It’s all helper code, so it’s perfectly allowable to do something like this.