Click here to Skip to main content
15,886,110 members
Articles / Programming Languages / Javascript

Integrating Silverlight with the Browser's Back Button

Rate me:
Please Sign up or sign in to vote.
4.00/5 (3 votes)
11 May 2009CPOL3 min read 29K   15   4
Describes how to make Silverlight applications behave like a traditional web application - with navigation states that use the browser's back/forwards navigation buttons.

Introduction

Silverlight is a great technology for bringing richer user experiences to the web client. But because Silverlight is a control on a webpage, scenarios can arise in which a logical navigation has occurred within the Silverlight control (for example, the user has clicked a button which causes a panel to update) but for which no actual webpage navigation has occurred (a problem familiar to AJAX developers). Because the web's dominant paradigm is one of a navigation stack, the user expects to be able to "go back" by pressing the large, convenient Back button as happens on most other websites that they have visited. Most displeasingly, doing so can navigate them away from the control entirely, to the previous page they were visiting, thus destroying all the state that existed in the control.

Various techniques have been developed to try to fit the square peg of expected user behavior predicated on Web 1.0 into the round hole of Web 2.0 style technology that does not use the corresponding browser navigation stack. Of these, perhaps the most elegant solution is to manipulate the browser's navigation stack - so that pressing the Back button indeed moves the Silverlight control "back" to the previous logical state. This completely conforms to the user's expectations of how the web behaves, and has the additional advantage of colonizing the browser's toolbar area for application specific logical commands (back and forwards buttons might otherwise end up on the control itself).

It turns out to be quite easy to manipulate the browser's navigation stack in this way, by means of a hidden IFRAME. Each logical navigation point is stored in this IFRAME and then restored to the control via JavaScript when the user presses the Back button. The good folks from the Yahoo User Interface (YUI) team have wrapped this use of an IFRAME within a convenient (free and unrestricted) JavaScript module that works on most browsers and platforms.

This article shows how to combine a Silverlight control that has distinct navigable states with the YUI library module to recreate the familiar web browser paradigm.

Using the Code

To use YUI History on your page, you first need to include the YUI JavaScript libraries. These can be obtained here.

XML
<script type="text/javascript" src="yui/yahoo-dom-event.js"></script>
<script type="text/javascript" src="yui/history-min.js"></script>

You also need to add the hidden IFRAME and hidden INPUT that will be used to store the control state:

XML
<style type="text/css">
#yui-history-iframe {
  position:absolute;
  top:0; left:0;
  width:1px; height:1px;
  visibility:hidden;
}
</style>
...
<iframe id="yui-history-iframe" src="blank.htm"></iframe>
<input id="yui-history-field" type="hidden"/>

Communication from JavaScript to the Silverlight control is through the JavaScriptBridge class. Whenever the browser's navigation state is changed, JavaScript will call the LoadState method.

C#
public class JavaScriptBridge
{
    Page _page;

    public JavaScriptBridge(Page page)
    {
        _page = page;
    }

    [ScriptableMember]
    public void LoadState(string state)
    {
        if (String.IsNullOrEmpty(state))
            _page.SetInitialState();
        else
            _page.LoadState(state);
    }
}

private void Application_Startup(object sender, StartupEventArgs e)
{
    Page page = new Page();
    this.RootVisual = page;
    JavaScriptBridge bridge = new JavaScriptBridge(page);
    HtmlPage.RegisterScriptableObject("jsBridge", bridge);
    string initialState = HtmlPage.Window.Invoke("GetInitialState") as string;
    if (initialState != null)
        bridge.LoadState(initialState);
}

Because there is a race condition between the YUI history manager and the Silverlight control's load event, there is a bit of logic to make sure that the control is always initialised with the initial start state (that may have been set via the query string).

The control calls GetInitialState, which returns the initial state if YUI has loaded, otherwise null. When YUI loads (_Init), it checks if the control has loaded. If so, it tells the control the page's initial state.

The YAHOO.util.Event.onDOMReady event handler will fire when the page's DOM has been loaded, and this is when the YUI history module is initialized.

JavaScript
var g_initialState = null, g_controlHasLoaded = false;

function GetInitialState() {
    g_controlHasLoaded = true;
    return g_initialState;
}

function LoadContent(state) {
    try {
        YAHOO.util.History.navigate("q", state.toString());
    } catch (e) {
        _LoadContent(state);
    }
}

function _LoadContent(state) {
    if (g_controlHasLoaded) {
        var ctrl = document.getElementById("silverlightControl");
        ctrl.Content.jsBridge.LoadState(state);
    } else
        g_initialState = state;
}

function _Init() {
    var state = YAHOO.util.History.getCurrentState("q");
    if (typeof (state) == "string") {
        if (g_controlHasLoaded)
            _LoadContent(state);
        else
            g_initialState = state;
    }
}

YAHOO.util.Event.onDOMReady(new function() {
    var bookmarkedState = YAHOO.util.History.getBookmarkedState("q");
    var queryState = YAHOO.util.History.getQueryStringParameter("q");
    var initialState = bookmarkedState || queryState || "";

    YAHOO.util.History.register("q", initialState, function(state) {
        _LoadContent(state);
    });

    YAHOO.util.History.onReady(function() {
        _Init();
    });

    // Initialize the browser history management library.
    try {
        YAHOO.util.History.initialize("yui-history-field", "yui-history-iframe");
    } catch (e) {
        _Init();
        // web navigation will not be available
    }
});

When the control wants to register a state transition, it calls the LoadContent JavaScript function. This will store the state in the IFRAME, and trigger a callback on the control, which the control then uses to transition to that state.

C#
internal void NavigateToState(string state)
{
    HtmlPage.Window.Invoke("LoadContent", state);
}

That's all there is to it. When running the sample application, you should make sure that you set "Default.aspx" as the start page, not the auto-generated HTML test page (which doesn't have the required JavaScript).

You'll notice that you can use the browser's Back and Forwards buttons to navigate between control states. You'll also be able to set the state via the query string. For this reason, you should always validate the incoming state to make sure it is valid.

History

  • 11 May 2009: First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Ice Blue Digital
Australia Australia
I am the founder of Ice Blue Digital - a Sydney based software company in the natural language processing and machine learning space.

Comments and Discussions

 
QuestionHow to Navigate to a page example Pin
rustamariyanur19-Aug-13 21:46
rustamariyanur19-Aug-13 21:46 
GeneralSilverlight 3 navigational framework Pin
Abhinav S2-Feb-10 8:58
Abhinav S2-Feb-10 8:58 
General[Message Deleted] Pin
RAND 45586619-May-09 0:00
RAND 45586619-May-09 0:00 
GeneralRe: SilverBrowserNavigation it is protected by the password. Is it a bug? Pin
Jack_Dermody19-May-09 10:55
Jack_Dermody19-May-09 10:55 
Not quite sure what you mean...

I was just able to download the demo project, and run it without any problem.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.