App Hub

Visuals & Media

Make your applications visually appealing with images, animations, and media.

Sensors & Other Phone Specific Features

Use unique features of the phone, such the accelerometer, GPS, and camera, in your applications.

Consuming a Windows Azure Data Service by using the OData Client
Silverlight quickstart for Windows Phone development

The Open Data Protocol (OData) is based on an entity and relationship model that enables you to access data in the style of representational state transfer (REST) resources. OData enables the use of the standard HTTP protocol to execute queries, and even to create, update, and delete data from a remote data service. By using the OData support in the Windows Phone SDK, your Windows Phone application can consume data from a Windows Azure service or any other service that supports the OData protocol. The client library lets you compose Language Integrated Query (LINQ) queries against an OData service and transforms the data in the response feed into objects on the client.

Note

This quickstart targets Windows Phone 7.5 development by using the Windows Phone SDK 7.1. This version of the Windows Phone SDK provides support for accessing data from Open Data Protocol (OData) feeds that was not provided in previous versions of the SDK.

This QuickStart contains the following sections:

The live sample in this QuickStart use Silverlight running in the browser to simulate the behavior in Silverlight for Windows Phone. The actual behavior may be slightly different in the Windows Phone emulator or on a Windows Phone device. You can download the completed project from Consuming a Windows Azure Data Service with the OData Client for Windows Phone on MSDN Developer Samples.


Overview of OData Services for Windows Phone

Mobile device applications rely heavily on remote data sources, and the Windows Azure platform provides an excellent source of data for your Windows Phone application. OData is one of the primary data access mechanisms of the Windows Azure platform. Various components of the Windows Azure platform produce OData feeds, including Windows Azure Table Storage, Microsoft SQL Azure, and Windows Azure Marketplace DataMarket. A comprehensive list of public OData feeds is available at the Open Data Protocol Web site.

You can also use WCF Data Services to expose your own data as OData feeds that can be consumed in applications that run on a variety of platforms, including Windows Phone. For more information, see WCF Data Services.

As an example, Netflix exposes their catalog of movies as a cloud-based OData feed. We consume this public OData feed in the following live sample that is the basis for this QuickStart:

Click the OData on Phone tile to start the live OData service sample. On the phone, when you tap the Next Page button the next page of data is loaded from the data service. When you tap an item in the list, detailed information about the selected title is displayed. In this example, the Back button works like it does on the phone by navigating you back to the previous page. You can download the complete project for this Windows Phone application from the Silverlight for Windows Phone project in the MSDN Code Gallery Web site.


Binding Data to Controls

In the OData client library for Windows Phone, the DataServiceCollection class represents a dynamic data binding collection that provides notifications when items get added to or removed from the collection. A URI-based query determines which data objects the binding collection will contain. This URI is specified as a parameter in the LoadAsync method of the DataServiceCollection class. When executed, this method returns an OData feed that is materialized into data objects in the binding collection. The materialized objects are managed by the DataServiceContext that is associated with the binding collection. The LoadAsync method of the DataServiceCollection class ensures that the results are marshaled to the correct thread, so you do not need to use a Dispatcher object. For more information about working with the OData client library for Windows Phone, see Open Data Protocol (OData) Overview for Windows Phone.

Because Windows Phone applications require navigation between multiple pages, you should use a Model-View-ViewModel (MVVM) design pattern for your data applications. In this pattern, the model is generated by tools based on metadata returned by the data service, the view is comprised of all the data bound controls on the page, and the ViewModel is a shared component that does the work of accessing the data service and exposing the data that is bound to the view. By using this approach, you can expose the DataServiceContext as a property of the view model class, along with properties that return DataServiceCollection instances or any other dynamic values that are bound to controls in the view. The view model must also expose any properties that need to be stored when the application is deactivated, which we will discuss in a later section. The following object diagram represents the MainViewModel class for this example:

MainViewModel class members

The following example shows the definitions for the properties in the MainViewModel class that expose DataServiceContext and DataServiceCollection instances:

C#

// Defines the root URI of the data service.
private static readonly Uri rootUri = new Uri("http://odata.netflix.com/v1/Catalog/");
// Define the typed DataServiceContext.
private NetflixCatalog _context;
// Define the binding collection for Titles.
private DataServiceCollection<Title> _titles;
// Gets and sets the collection of Title objects from the feed.
public DataServiceCollection<Title> Titles
{
get { return _titles; }
private set
{
// Set the Customers collection.
_titles = value;
// Register a handler for the LoadCompleted callback.
_titles.LoadCompleted += OnTitlesLoaded;
// Raise the PropertyChanged events.
NotifyPropertyChanged("Titles");
}
}

Visual Basic

' Defines the root URI of the data service.
Private Shared ReadOnly _rootUri As Uri = New Uri("http://odata.netflix.com/v1/Catalog/")
Private _isDataLoaded As Boolean
' Define the typed DataServiceContext.
Private _context As NetflixCatalog
' Define the binding collection for Titles.
Private _titles As DataServiceCollection(Of Title)
' Gets and sets the collection of Title objects from the feed.
Public Property Titles As DataServiceCollection(Of Title)
Get
Return _titles
End Get
Set(value As DataServiceCollection(Of Title))
' Set the Titles collection.
_titles = value
' Register a handler for the LoadCompleted callback.
AddHandler _titles.LoadCompleted, AddressOf OnTitlesLoaded
' Raise the PropertyChanged events.
NotifyPropertyChanged("Titles")
End Set
End Property

The MainViewModel class itself is exposed as a static property of the root application class (app), as is shown in the following:

C#

static MainViewModel _viewModel = null;
// A static ViewModel used by the views to bind against.
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until we need it.
if (_viewModel == null)
{
_viewModel = new MainViewModel();
}
return _viewModel;
}
}

Visual Basic

Shared _viewModel As MainViewModel = Nothing
' A static ViewModel used by the views to bind against.
Public Shared ReadOnly Property ViewModel As MainViewModel
Get
' Delay creation of the view model until we need it.
If _viewModel Is Nothing Then
_viewModel = New MainViewModel()
End If
Return _viewModel
End Get
End Property

The view model is set as the DataContext of the page, which enables us to bind the ListBox control in MainPage.xaml to the DataServiceCollection returned by the Titles property of the MainViewModel class. You can see in the following XAML example that the ListBox and a few other elements are bound to properties of the MainViewModel:

XAML

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox Margin="0,0,-12,0" ItemsSource="{Binding Titles}"
SelectionChanged="OnSelectionChanged" Height="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Orientation="Horizontal">
<Image Source="{Binding Path=StreamUri}"
Height="75" Width="50" />
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<converter:TruncateSynopsis x:Key="synopsis" />
</StackPanel.Resources>
<TextBlock Text="{Binding Path=ShortName}" TextWrapping="Wrap"
Style="{StaticResource PhoneTextLargeStyle}"/>
<TextBlock Text="{Binding Path=ShortSynopsis,
Converter={StaticResource synopsis}}"
TextWrapping="Wrap" Margin="12,-6,12,0"
Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<TextBlock Name="PagingInfo" Text="{Binding PagesLoadedText}" Margin="12,20,10,28"
Width="270" Style="{StaticResource PhoneTextNormalStyle}" />
<Button Name="MoreButton" Content="Next Page"
HorizontalAlignment="Right" Width="185" Height="80" Click="MoreButton_Click" />
</StackPanel>

When the MainPage.xaml page loads, the IsDataLoaded property on the view model is checked. When the data is not already loaded, the LoadData method on the view model is called to set the binding objects and request titles from the data service.

C#

// Used to determine whether the data is loaded.
public bool IsDataLoaded { get; private set; }
// Loads data when the application is initialized.
public void LoadData()
{
// Instantiate the context and binding collection.
context = new NetflixCatalog(_rootUri);
// Use the public property setter to generate a notification to the binding.
Titles = new DataServiceCollection<Title>(_context);
// Load the data.
titles.LoadAsync(GetQuery());
}

Visual Basic

' Used to determine whether the data is loaded.
Public Property IsDataLoaded As Boolean
Get
Return _isDataLoaded
End Get
Private Set(value As Boolean)
isDataLoaded = value
End Set
End Property
' Loads data when the application is initialized.
Public Sub LoadData()
' Instantiate the context and binding collection.
context = New NetflixCatalog(_rootUri)
titles = New DataServiceCollection(Of Title)(_context)
' Load the data.
titles.LoadAsync(GetQuery())
End Sub

When the LoadAsync method is called, the client library asynchronously sends a query request to the OData service that returns an Atom feed of Title entries. When the response is received, the LoadCompleted event is raised and the entries in the feed are materialized by the client library into objects in the Titles collection, which is bound to the ListBox control.

OData defines a mechanism for accessing binary data separate from the entity to which it belongs. In this way, a data service can expose large binary data as a media resource that belongs to a media link entry. In the Netflix catalog, the Title entity is a media link entry with a related media resource, which is the box art for the title. The GetReadStreamUri method on the view model calls the GetReadStreamUri method on the DataServiceContext to return the URI of the media resource. The following example shows the definition of a StreamUri extension property that returns the URI of the media resource for binding:

C#

// Extend the Title class to bind to the media resource URI.
public partial class Title
{
// Returns the media resource URI for binding.
public Uri StreamUri
{
get
{
// Get the URI for the media resource stream.
return App.ViewModel.GetReadStreamUri(this);
}
}
}

Visual Basic

' Extend the Title class to bind to the media resource URI.
Partial Public Class Title
' Returns the media resource URI for binding.
Public ReadOnly Property StreamUri As Uri
Get
' Get the URI for the media resource stream.
Return App.ViewModel.GetReadStreamUri(Me)
End Get
End Property
End Class

In this example, the StreamUri property calls the GetReadStreamUri method to return the media resource URI, which is the box art image. When we bind the StreamUri property to the Source attribute of an Image control, the returned URI is used to download and display the box art for each Title.

Recommendation

For a media link entry, we recommend that you get the URI of the media resource by calling GetReadStreamUri rather than from some other property on the entity. Although the URI of the media resource works well in this case to create an image on the client, other DataServiceContext methods can be used to access and change media resources as a binary stream.


Paging and Navigation

In this example, a typed DataServiceQuery is constructed by using LINQ and the paging parameters defined in the application. In OData there are two different kinds of paging. A client can use the Take and Skip LINQ methods to limit the number of entries in the response to a logical page instead of the full feed. These LINQ query methods are translated by the OData library into $top and $skip system query options in the request URI. The data service itself can also limit the number of entries returned in a given response, where the response contains a continuation token that is used to get the next page of data from the data service.

Because the client is not able to determine whether a data service implements server-driven paging or even the page size, you should consider implementing client paging in your Windows Phone applications to prevent a query returning a very large feed, which can take time and consume valuable phone resources. You should also be prepared to handle server-driven paging in your application.

The following shows the GetQuery method from this example that returns the strongly-typed DataServiceQuery, which is created based on variables passed to the Take and Top parameters.

C#

// Private method that returns the page-specific query.
private DataServiceQuery<Title> GetQuery()
{
// Get a query for the Titles feed from the context.
DataServiceQuery<Title> query = _context.Titles;
if (_currentPage == 0)
{
// If this is the first page, then also include a count of all titles.
query = query.IncludeTotalCount();
}
// Add paging to the query.
query = query.Skip(_currentPage * _pageSize)
.Take(_pageSize) as DataServiceQuery<Title>;
return query;
}

Visual Basic

' Private method that returns the page-specific query.
Private Function GetQuery() As DataServiceQuery(Of Title)
' Get a query for the Titles feed from the context.
Dim query As DataServiceQuery(Of Title) = _context.Titles
If _currentPage.Equals(0) Then
' If this is the first page, then also include a count of all titles.
query = query.IncludeTotalCount()
End If
' Add paging to the query.
query = TryCast(query.Skip(_currentPage * _pageSize) _
.Take(_pageSize), DataServiceQuery(Of Title))
Return query
End Function

In this example, the page size is fixed, and the number of entries to skip is calculated based on the current page count. When we load the first page, we also call the IncludeTotalCount method on the query to request a total count of all Title entities, which is used to calculate total number of pages that is displayed in the TitlesPage.xaml. Calling the IncludeTotalCount method adds the $inlinecount=allpages system query option to the query URI generated by the library.

When the response is received, the client raises the LoadCompleted event. When there are additional pages to load because of server-driven paging, the Continuation property of the DataServiceCollection returns a continuation token. The following method handles the LoadCompleted event to load all server-driven pages:

C#

private void OnTitlesLoaded(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
// Make sure that we load all pages of the Customers feed.
if (Titles.Continuation != null)
{
Titles.LoadNextPartialSetAsync();
}
// Set the total page count, if we requested one.
if (e.QueryOperationResponse.Query
.RequestUri.Query.Contains("$inlinecount=allpages"))
{
totalCount = (int)e.QueryOperationResponse.TotalCount;
}
IsDataLoaded = true;
// Update the pages loaded text binding.
NotifyPropertyChanged("PagesLoadedText");
}
else
{
// Display the error message in the binding.
this.Message = e.Error.Message;
}
}

Visual Basic

Private Sub OnTitlesLoaded(ByVal sender As Object, ByVal e As LoadCompletedEventArgs)
If e.Error Is Nothing Then
' Make sure that we load all pages of the Customers feed.
If Not titles.Continuation Is Nothing Then
titles.LoadNextPartialSetAsync()
End If
' Set the total page count, if we requested one.
If e.QueryOperationResponse.Query _
.RequestUri.Query.Contains("$inlinecount=allpages") Then
totalCount = CType(e.QueryOperationResponse.TotalCount, Integer)
End If
IsDataLoaded = True
' Update the pages loaded text binding.
NotifyPropertyChanged("PagesLoadedText")
Else
' Display the error message in the binding.
Me.Message = e.Error.Message
End If
End Sub

Note that we also read and store the TotalCount value when it is requested in the query. This value is used to calculate and display the total number of pages. Because an updated TotalCount changes the value of the PagesLoadedText property, we also report this change to the binding.

The next logical page of data is requested when the user presses the Next Page button in MainPage.xaml. While you could create a new DataServiceCollection based on the URI that returns the next page, the following example instead uses navigation to load a specific page of data:

C#

private void MoreButton_Click(object sender, RoutedEventArgs e)
{
if (App.ViewModel.IsDataLoaded)
{
// Navigate to the next page of data.
this.NavigationService.Navigate(
new Uri("/TitlesPage.xaml?page="
+ (App.ViewModel.CurrentPage + 1), UriKind.Relative));
}
}

Visual Basic

Private Sub MoreButton_Click(sender As Object, e As RoutedEventArgs)
If App.ViewModel.IsDataLoaded Then
' Navigate to the next page of data.
Me.NavigationService.Navigate(New Uri("/TitlesPage.xaml?page=" & (App.ViewModel.CurrentPage + 1), UriKind.Relative))
End If
End Sub

This navigation essentially reloads the MainPage.xaml page with the page count of the next page. When the MainPage.xaml page is navigated to, a specific page of data is loaded from the data service, as follows:

C#

protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
else
{
if (this.NavigationContext.QueryString.Count == 1)
{
// Get the value of the requested page.
int page = int.Parse(this.NavigationContext.QueryString["page"]);
// Check to see if the page is currently loaded.
if (page != App.ViewModel.CurrentPage)
{
// Load data for the specific page.
App.ViewModel.LoadData(page);
}
}
else
{
// If there is no query parameter, we are at the first page.
App.ViewModel.LoadData(0);
}
}
}

Visual Basic

Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
If Not App.ViewModel.IsDataLoaded Then
App.ViewModel.LoadData()
Else
If Me.NavigationContext.QueryString.Count = 1 Then
' Get the value of the requested page.
Dim page As Integer = Integer.Parse(Me.NavigationContext.QueryString("page"))
' Check to see if the page is currently loaded.
If page <> App.ViewModel.CurrentPage Then
' Load data for the specific page.
App.ViewModel.LoadData(page)
End If
Else
' If there is no query parameter, we are at the first page.
App.ViewModel.LoadData(0)
End If
End If
End Sub

By using navigation to load subsequent pages, when the user presses the Back button the default behavior is to reload the previous page. Without the navigation, you would have to override the OnBackKeyPress method to load the previous page of data.

When the user presses a Title in the ListBox control, the following method navigates to the TitlesDetailPage.xaml for the selected Title:

C#

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selector = (Selector)sender;
if (selector.SelectedIndex == -1)
{
return;
}
this.NavigationService.Navigate(
new Uri("/TitleDetailsPage.xaml?selectedIndex="
+ selector.SelectedIndex, UriKind.Relative));
selector.SelectedIndex = -1;
}

Visual Basic

Private Sub OnSelectionChanged(sender As Object, e As SelectionChangedEventArgs)
Dim selector = DirectCast(sender, Selector)
If selector.SelectedIndex = -1 Then
Return
End If
Me.NavigationService.Navigate(New Uri("/TitleDetailsPage.xaml?selectedIndex=" & Convert.ToString(selector.SelectedIndex), UriKind.Relative))
selector.SelectedIndex = -1
End Sub

In the TitleDetailsPage.xaml page, the selected index value is used to bind the correct Title from the Titles collection, as follows:

C#

protected override void OnNavigatedTo(NavigationEventArgs e)
{
string indexAsString = this.NavigationContext.QueryString["selectedIndex"];
int index = int.Parse(indexAsString);
this.DataContext = this.currentTitle
= (Title)App.ViewModel.Titles[index];
}

Visual Basic

Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
Dim indexAsString As String = Me.NavigationContext.QueryString("selectedIndex")
Dim index As Integer = Integer.Parse(indexAsString)
Me.DataContext = InlineAssignHelper(Me.currentTitle, DirectCast(App.ViewModel.Titles(index), Title))
End Sub

Maintaining Application State

Applications are typically put into a dormant state when the user navigates away. In this state, the application is preserved in memory so that if the user returns to the application, it can resume almost instantly. This fast application switching is enabled automatically. However, it is still possible that an application will be terminated while it is dormant. To enable the application to be reactivated correctly, loaded data, object state information, and certain view model properties must be stored in the state dictionary. The OData client for Windows Phone includes a DataServiceState class that is used to serialize the DataServiceContext and a collection of DataServiceCollection objects so that this data can be stored in a state dictionary. The following SaveState method in the view model returns the serialized state as a string object in a key-value pair collection of items, along with other view model data that must be persisted:

C#

// Return a collection of key-value pairs to store in the application state.
public List<KeyValuePair<string, object>> SaveState()
{
if (App.ViewModel.IsDataLoaded)
{
List<KeyValuePair<string, object>> stateList
= new List<KeyValuePair<string, object>>();
// Create a new dictionary to store binding collections.
var collections = new Dictionary<string, object>();
// Add the current Titles binding collection.
collections["Titles"] = App.ViewModel.Titles;
// Store the current context and binding collections in the view model state.
stateList.Add(new KeyValuePair<string, object>(
"DataServiceState", DataServiceState.Serialize(_context, collections)));
stateList.Add(new KeyValuePair<string, object>("CurrentPage", CurrentPage));
stateList.Add(new KeyValuePair<string, object>("TotalCount", TotalCount));
return stateList;
}
else
{
return null;
}
}

Visual Basic

' Return a collection of key-value pairs to store in the application state.
Public Function SaveState() As List(Of KeyValuePair(Of String, Object))
If App.ViewModel.IsDataLoaded Then
Dim stateList As New List(Of KeyValuePair(Of String, Object))()
' Create a new dictionary to store binding collections.
Dim collections = New Dictionary(Of String, Object)()
' Add the current Titles binding collection.
collections("Titles") = App.ViewModel.Titles
' Store the current context and binding collections in the view model state.
stateList.Add(New KeyValuePair(Of String, Object)("DataServiceState", DataServiceState.Save(_context, collections)))
stateList.Add(New KeyValuePair(Of String, Object)("CurrentPage", CurrentPage))
stateList.Add(New KeyValuePair(Of String, Object)("TotalCount", TotalCount))
Return stateList
Else
Return Nothing
End If
End Function

The following method handles the Deactivated event and calls the SaveState method to store view model data when the application is deactivated:

C#

// Code to execute when the application is deactivated (sent to background).
// This code will not execute when the application is closing.
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
if (App.ViewModel.IsDataLoaded)
{
// Store each key-value pair in the state dictionary.
foreach (KeyValuePair<string, object> item in App.ViewModel.SaveState())
{
PhoneApplicationService.Current.State[item.Key] = item.Value;
}
}
}

Visual Basic

' Code to execute when the application is deactivated (sent to background).
' This code will not execute when the application is closing.
Private Sub Application_Deactivated(sender As Object, e As DeactivatedEventArgs)
If App.ViewModel.IsDataLoaded Then
' Store each key-value pair in the state dictionary.
For Each item As KeyValuePair(Of String, Object) In App.ViewModel.SaveState()
PhoneApplicationService.Current.State(item.Key) = item.Value
Next
End If
End Sub

When the application is reactivated, view model is retrieved from the state dictionary. The following method handles the Activated event to restore the application to the previous state:

C#

// Code to execute when the application is activated (brought to foreground).
// This code will not execute when the application is first launched.
private void Application_Activated(object sender, ActivatedEventArgs e)
{
object viewModelState;
if (PhoneApplicationService.Current.State.TryGetValue("ViewModelState", out viewModelState))
{
App.ViewModel.RestoreState(viewModelState as IDictionary<string,object>);
}
}

Visual Basic

' Code to execute when the application is activated (brought to foreground).
' This code will not execute when the application is first launched.
Private Sub Application_Activated(sender As Object, e As ActivatedEventArgs)
App.ViewModel.RestoreState(PhoneApplicationService.Current.State)
End Sub

In the following RestoreState method, the DataServiceState object and other stored data are acquired from the global state dictionary and used to reinitialize the view model:

C#

// Restores the view model state from the supplied state dictionary.
public void RestoreState(IDictionary<string, object> dictionary)
{
// Create a dictionary to hold any stored binding collections.
object titles;
object stateAsString;
if (dictionary.TryGetValue("DataServiceState", out stateAsString))
{
// Rehydrate the DataServiceState object from the serialization.
DataServiceState state =
DataServiceState.Deserialize((string)stateAsString);
if (state.RootCollections.TryGetValue("Titles", out titles))
{
// Initialize the application with data from the DataServiceState.
App.ViewModel.LoadData((NetflixCatalog)state.Context,
(DataServiceCollection<Title>)titles);
// Restore other view model data.
_currentPage = (int)dictionary["CurrentPage"];
_totalCount = (int)dictionary["TotalCount"];
}
}
}

Visual Basic

' Restores the view model state from the supplied state dictionary.
Public Sub RestoreState(dictionary As IDictionary(Of String, Object))
' Create a dictionary to hold any stored binding collections.
Dim collections As Dictionary(Of String, Object)
' Get the stored DataServiceState object from the dictionary.
Dim state = TryCast(dictionary("DataServiceState"), DataServiceState)
If state IsNot Nothing Then
' Restore the context and binding collections.
Dim context As NetflixCatalog = TryCast(state.Restore(collections), NetflixCatalog)
' Get the binding collection of Title objects.
Dim titles As DataServiceCollection(Of Title) = TryCast(collections("Titles"), DataServiceCollection(Of Title))
' Initialize the application with stored data.
App.ViewModel.LoadData(context, titles)
' Restore other view model data.
_currentPage = CInt(dictionary("CurrentPage"))
_totalCount = CInt(dictionary("TotalCount"))
End If
End Sub

Generating the Client Data Classes

You can use the Add Service Reference dialog box in Visual Studio to add a reference to any service that exposes an OData feed. This tool connects to the data service and generates the data classes and the data container, which inherits from the DataServiceContext class. The following is how the Add Service Reference dialog box looks when you add a reference to the Netflix OData service:

Add Service Reference

See Also

var gDomain='m.webtrends.com'; var gDcsId='dcschd84w10000w4lw9hcqmsz_8n3x'; var gTrackEvents=1; var gFpc='WT_FPC'; /*<\/scr"+"ipt>");} /*]]>*/
DCSIMG