Passing Data Between Views

In standard web development, HTML pages are essentially stateless—that is, when the server processes each page request, it is not aware of the requests that came before it. Session variables, cookies, and ASP.NET's view state are workarounds to this issue that simulate statefulness as you navigate through a web site. To pass data to another page in a request, you would usually pass this as query string parameters appended to the end of the URL of the page that accepts the data. Alternatively, you may post complex state data containing state information, such as view state, back to the server to be processed.

By existing entirely on the client side, Silverlight applications aren't subject to this problem of statelessness and are inherently stateful, in the same way as a rich desktop application is stateful. The state of the application is maintained as the user navigates between the views. However, it would not be particularly good design to maintain state information belonging to a small set of views at a global level simply as a means of passing state data between them. Since each view using the navigation framework has an associated URI, the navigation framework follows the standard URI pattern and enables you to include query strings in them, which you can harness to pass data between the views.

Passing Data Using Query String Parameters

Query string parameters enable you to pass simple data types (strings, integers, and so on) from one view to another as you navigate between them. For example, you might want to pass the ID of a selected product from a view displaying a list of products to the view displaying the details of that product.

The query string parameters are appended to the end of the bookmark in the URI when navigating to a view that will consume them. The format that these normally take is:

/Views/ProductDetailsView.xaml?ProductID=879

In this example, we are passing the ProductDetailsView view a query string containing a parameter named ProductID and with a value of 879. Note that a question mark, which indicates the start of the query string, is appended to the URI, followed by the parameter name and its value, separated by an equal sign.

You can pass multiple parameters to a view, separating each with an ampersand (&). The following example demonstrates passing the ProductDetailsView view both a ProductID and a SupplierID:

/Views/ProductDetailsView.xaml?ProductID=879&SupplierID=437

Depending on your URI mapping scheme, the query string parameters don't need to take this extended form (ParameterName=Value). Instead, we could use a much friendlier URI in place of the one from the first example, such as:

ProductDetails/879

This URI will be mapped and parsed internally in the application using the specified URI mapping scheme to convert it to its standard form. How this is achieved will be discussed later in this chapter when we cover URI mapping.

Reading Query String Parameters

When a view needs to read the parameters passed to it in its query string, it can use the QueryString property on the view's NavigationContext object. This is an indexed collection, where the index is the name of the property you wish to retrieve the value for, for example:

object productID = NavigationContext.QueryString["ProductID"];

One of the problems with this code is that it returns an object that you will need to cast to the required type. However, if that parameter wasn't provided in the query string, a null value will be returned, and there's no guarantee that it will be of the correct type (requiring additional code to check for this before attempting to convert it).

Parsing query string parameters each time you use them makes for messy code that is hard to read and maintain. This process generally requires you to do the following:

  • Ensure the parameter exists.
  • Check that the value is of the correct type.
  • Convert the value from a string to the required type.

The best way to handle this parsing process is to require that all expected query string parameters have a corresponding strongly typed property (getter only) in the view's code-behind and return the value as the correct type. If a required parameter has not been passed in or is malformed—that is, cannot be converted to the required type or contains an invalid value—it should return a default value or throw an exception. Wrapping the retrieval of a query string parameter in a property this way makes for much cleaner and more maintainable code. For example, the following code is used to handle access to the ProductID parameter within a view. As you can see, it checks to see whether the value has been passed in and then parses out the value as an integer and returns it. Whenever you need the value of the ProductID query parameter, you can now simply get it from the ParamProductID property.

private int ParamProductID
{
    get
    {
        int paramValue = 0;
        const string paramName = "ProductID";

        if (NavigationContext.QueryString.ContainsKey(paramName))
            int.TryParse(NavigationContext.QueryString[paramName], out paramValue);
        return paramValue;
    }
}

Passing Complex Data Types Between Views

As you've seen, simple data types can be passed easily between views using query string parameters. However, what if you wanted to pass some more complex data types, such as objects? Say, for example, you wanted to pass the entire product object to the product details view instead of just its ID. You might be able to serialize the product object to a string and pass that as a parameter in the query string, but this idea is exceedingly messy and open to exceeding the maximum length of an acceptable URL, which depends on the browser—but the general recommendation is that it should not exceed approximately 2,000 characters, and the usual advice for user-friendliness is to keep it short. Of course, we could create an application-wide cache to store objects in and retrieve them from, but this design isn't particularly nice or robust.

Unfortunately, the Silverlight navigation framework has no built-in means to post or pass complex data types between views, without resorting to storing them globally in an application-wide cache before navigating away from a view and then retrieving and removing them from the cache when the destination view has loaded. Ideally, an additional parameter to the NavigationService.Navigate method would take an object to which you could attach your state-based values and then pass directly to the view being navigated to, but sadly, this is not the case. The navigation framework is a part of the Silverlight Toolkit, enabling you to add the behavior yourself if you were so inclined. However, modifying a framework like this—where the change cannot be submitted back to the original project, because Microsoft maintains the Silverlight Toolkit project and is the only contributor—is not generally good practice. It makes your implementation nonstandard, preventing you from easily updating to new releases in your project. In Chapter 13, we'll discuss the MVVM pattern, which provides an alternative solution to global variables by maintaining the state in a ViewModel and making it accessible to multiple views.

images Note You should be wary when using the object serialization method of passing complex data between views in a query string parameter. Users may potentially alter the query string in the browser's address bar (or exclude it altogether). They also may potentially bookmark a view with data in the query string and come back to it later, when the data may no longer be valid. Checks should be made to ensure that expected data is available and valid, and situations in which it's not should be handled.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset