Skip to content

Dynamic Navigation Controls

craigomatic edited this page Sep 2, 2014 · 4 revisions

AppBar and other native UI controls are a great way to make a website feel more like an app.

The simplest way to add them in is to hardcode the buttons and hardcode the routes those buttons map to. This approach (while brittle) will usually be ok as website navigation doesn't change frequently.

However you might decide that you want to future proof the app or perhaps the website is localised into many different languages and you want to make sure the users in those regions get the correct language strings.

While this approach will vary for each website, the following is the general approach:

Step 1: Read the menu structure

If the website is written using a modern framework (ie: Backbone.js), you might be able to pull this information from one of the JS model objects available to you at runtime. If not, you'll need to process the DOM. This sample will focus on processing the DOM.

In app.js define the following function:

app.readMenu = function() {
    var structure = [];
    var menuItems = document.querySelectorAll('.menu-item a');

    for (var i = 0; i < menuItems.length; i++) {
        var anchorItem = menuItems.item(i);

        var navItem = {};
        navItem.title = anchorItem.innerText;
        navItem.href = anchorItem.href;
        
        structure.push(navItem);
    }

    var msg = {};
    msg.type = 'menu';
    msg.payload = JSON.stringify(structure);

    framework.scriptNotify(JSON.stringify(msg));
}

Note: This function is designed to retrieve the menu from the standard Wordpress TwentyFourteen theme. Other websites will require a derivative of the above.

Step 2: Handle the message

Assuming you're using the HybridWebView Control, place the following in the MessageReceived event handler:

private async void WebHost_MessageReceived(HybridWebView sender, ScriptMessage msg)
{
    switch (msg.Type)
    {
        case KnownMessageTypes.Menu:
            {
                //generate a navbar based on this
                var navItems = await Task.Factory.StartNew(() => JsonConvert.DeserializeObject<IList<NavItem>>(msg.Payload));

                foreach (var item in navItems)
                {
                    var button = new Button
                    {
                        Content = item.Title,
                    };

                    button.Click += (s, a) => { WebHost.Navigate(new Uri(item.Href)); };

                    AppBarItemsHost.Children.Add(button);
                }

                break;
            }
    }
}

Step 3: Add a mapping to the Ready event

Handle the Ready event and add a "run-once" mapping that invokes the readMenu script created in the first script.

private void WebHost_Ready(object sender, EventArgs e)
{
    WebHost.WebRoute.Map("/", async (uri, success, errorCode) =>
    {
        await WebHost.Interpreter.EvalAsync("app.readMenu();");
    }, true);
}

Step 4: Hide the website menu structure:

In app.css add the following style:

#masthead {
    display: none;
}

Sample Code

See the Universal Apps sample in the repository.