{"id":953,"date":"2011-05-17T12:02:56","date_gmt":"2011-05-17T12:02:56","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/odatateam\/2011\/05\/17\/accessing-an-odata-media-resource-stream-from-a-windows-phone-7-application-streaming-provider-series-part-3\/"},"modified":"2024-02-16T13:10:38","modified_gmt":"2024-02-16T20:10:38","slug":"accessing-an-odata-media-resource-stream-from-a-windows-phone-7-application-streaming-provider-series-part-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/odata\/accessing-an-odata-media-resource-stream-from-a-windows-phone-7-application-streaming-provider-series-part-3\/","title":{"rendered":"Accessing an OData Media Resource Stream from a Windows Phone 7 Application (Streaming Provider Series-Part 3)"},"content":{"rendered":"<p>In this third post in the series on implementing a streaming data provider, we show how to use the OData client library for Windows Phone 7 to asynchronously access binary data exposed by an Open Data Protocol (OData) feed. We also show how to asynchronously upload binary data to the data service. This Windows Phone sample is the asynchronous equivalent to the previous post <a href=\"http:\/\/blogs.msdn.com\/b\/astoriateam\/archive\/2010\/09\/08\/data-services-streaming-provider-series-part-2-accessing-a-media-resource-stream-from-the-client.aspx\">Data Services Streaming Provider Series-Part 2: Accessing a Media Resource Stream from the Client<\/a>; both client samples access the streaming provider that we create in the first blog post in this series: <font color=\"#ff0000\"><a href=\"https:\/\/go.microsoft.com\/fwlink\/?LinkID=198989\">Implementing a Streaming Provider<\/a><\/font>. This post also assumes that you are already somewhat familiar with using the OData client library for Windows Phone 7 (which you can obtain from the <a href=\"https:\/\/odata.codeplex.com\/releases\/view\/54698\">OData project in CodePlex<\/a>), as well as phone-specific concepts like paged navigation and tombstoning. For more information about OData and Windows Phone, see the topic <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/gg521146(v=VS.92).aspx\">Open Data Protocol (OData) Overview for Windows Phone<\/a>. <\/p>\n<h3>OData Client Programming for Windows Phone 7 <\/h3>\n<p>This application consumes an OData feed exposed by the sample photo data service, which implements a streaming provider to store and retrieve image files, along with information about each photo. This service returns a single feed (entity set) of PhotoInfo entries, which are also media link entries. The associated media resource for each media link entry is an image, which can be downloaded from the data service as a stream. The following represents the PhotoInfo entity in the data model:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/5265.image_2C2FBB52.png\" \/><\/p>\n<p>This sample streaming data service is demonstrated in <font color=\"#ff0000\"><a href=\"https:\/\/go.microsoft.com\/fwlink\/?LinkID=198989\">Implementing a Streaming Provider<\/a><\/font>. You can download this streaming data service as a Visual Studio project from <a href=\"http:\/\/code.msdn.microsoft.com\/Streaming-Photo-OData-7feb9239\">Streaming Photo OData Service Sample<\/a> on MSDN Code Gallery. In our client phone application, we bind data from the PhotoInfo feed to UI controls in the XAML page. <\/p>\n<p>First we need to create a Window Phone application that references the OData client library. (Note that the same basic APIs can be used to access and create media resources from a Silverlight client, except for the tombstoning functionality, which is specific to Windows Phone.) I won\u2019t go into too much detail on the XAML that creates the pages in the application, since this is not a tutorial on XAML. You can review for yourself the XAML pages in the downloaded ODataStreamingPhoneClient project.<font style=\"background-color: #ffffff\"> Here<\/font> are the basic steps to create this application<font style=\"background-color: #ffffff\">:<\/font><\/p>\n<ol>\n<li>Download and install the <a href=\"https:\/\/odata.codeplex.com\/releases\/view\/54698\">OData client library for Windows Phone 7<\/a>. This includes the System.Data.Services.Client.dll assembly and the DataSvcUtil.exe tool. <\/li>\n<li>Create the Windows Phone project. <\/li>\n<li>Run the DataSvcUtil.exe program (included in the <a href=\"https:\/\/odata.codeplex.com\/releases\/view\/54698\">OData client library for Windows Phone 7<\/a> download) to generate the client data classes for the data service.       <br \/>Your command line should look like this (except all on one line):       \n<blockquote>\n<p><font style=\"background-color: #ffffff\"><font size=\"1\"><font face=\"Courier New\">DataSvcUtil.exe \/out:&quot;PhotoData.cs&quot; \/language:csharp \/DataServiceCollection                <br \/>\/uri:<\/font><font face=\"Courier New\">http:\/\/myhostserver\/PhotoService\/PhotoData.svc\/<\/font><\/font><\/font><font style=\"background-color: #ffffff\" size=\"1\" face=\"Courier New\"> \/version:2.0<\/font><\/p>\n<\/blockquote>\n<\/li>\n<li>Add a reference to the System.Data.Services.Client.dll assembly. <\/li>\n<li>Create a ViewModel class for the application named MainViewModel. This ViewModel helps connect the view (controls in XAML pages) to the model (OData feed accessed using the client library) by exposing properties and methods required for data binding and tombstoning. The following represents the MainViewModel class that supports this sample:      <br \/><a href=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/0042.image_thumb_618CC20B.png\"><img decoding=\"async\" style=\"border-bottom: 0px;border-left: 0px;margin: 12px 0px;padding-left: 0px;padding-right: 0px;border-top: 0px;border-right: 0px;padding-top: 0px\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/0042.image_thumb_618CC20B.png\" width=\"494\" height=\"345\" \/><\/a> <\/li>\n<li>Implement tombstoning to store application state when the application is deactivated and restore state when the application is reactivated. This is important because deactivation can happen at any time, including when the application itself displays the PhotoChooserTask to select a photo stored on the phone. To learn more about how to tombstone using the DataServiceState object, see <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/gg521146(VS.92).aspx\">Open Data Protocol (OData) Overview for Windows Phone<\/a>. <\/li>\n<li>The MainPage.xaml page displays a ListBox of PhotoInfo objects, which includes the media resources as images downloaded from the streaming data service.      <br \/><a href=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/4657.image_thumb_4DFF626A.png\"><img decoding=\"async\" style=\"border-right-width: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px;padding-top: 0px\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/4657.image_thumb_4DFF626A.png\" width=\"261\" height=\"480\" \/><\/a> <\/li>\n<li>When one of the items in the ListBox is tapped, details of the selected PhotoInfo object are displayed in a Pivot control the PhotoDetailsWindow page:      <br \/><a href=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/0435.image_thumb_2C3389D9.png\"><img decoding=\"async\" style=\"border-right-width: 0px;margin: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px;padding-top: 0px\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/0435.image_thumb_2C3389D9.png\" width=\"263\" height=\"480\" \/><\/a><a href=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/0842.image_thumb_4F144B89.png\"><img decoding=\"async\" style=\"border-right-width: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px;border-bottom-width: 0px;border-left-width: 0px;padding-top: 0px\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2011\/05\/0842.image_thumb_4F144B89.png\" width=\"260\" height=\"480\" \/><\/a>&#160; <\/li>\n<\/ol>\n<h3>Querying the Data Service and Binding the Streamed Data<\/h3>\n<p>The following steps are required to asynchronously query the streaming OData service. All code that access the OData service is implemented in the MainViewModel class.<\/p>\n<ol>\n<li>Declare the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.data.services.client.dataservicecontext\">DataServiceContext<\/a> used to access the data service and the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.data.services.client.dataservicecollection\">DataServiceCollection<\/a> used for data binding.       \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Declare the service root URI.            <br \/>private Uri svcRootUri =             <br \/>&#160;&#160;&#160; new Uri(serviceUriString, UriKind.Absolute);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Declare our private binding collection.            <br \/>private DataServiceCollection&lt;PhotoInfo&gt; _photos;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Declare our private DataServiceContext.            <br \/>private PhotoDataContainer _context;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">public bool IsDataLoaded { get; private set; }<\/font><\/p>\n<\/blockquote>\n<\/li>\n<li>Register a handler for the LoadCompleted event when the binding collection is set.&#160;&#160; \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">public DataServiceCollection&lt;PhotoInfo&gt; Photos            <br \/>{             <br \/>&#160;&#160;&#160; get { return _photos;}             <br \/>&#160;&#160;&#160; set             <br \/>&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; _photos = value;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160;&#160;&#160;&#160;&#160; NotifyPropertyChanged(&quot;Photos&quot;);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Register a handler for the LoadCompleted event.            <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; _photos.LoadCompleted +=             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; new EventHandler&lt;LoadCompletedEventArgs&gt;(Photos_LoadCompleted);             <br \/>&#160;&#160;&#160; }             <br \/>}<\/font><\/p>\n<\/blockquote>\n<\/li>\n<li>When MainPage.xaml is navigated to, the LoadData method on the ViewModel is called; the LoadAsync method asynchronously executes the query URI.      \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Instantiate the context and binding collection.            <br \/>_context = new PhotoDataContainer(svcRootUri);             <br \/>Photos = new DataServiceCollection&lt;PhotoInfo&gt;(_context);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Load the data from the PhotoInfo feed.            <br \/>Photos.LoadAsync(new Uri(&quot;\/PhotoInfo&quot;, UriKind.Relative));<\/font><\/p>\n<p><font size=\"2\" face=\"Courier New\"><\/font><\/p>\n<\/blockquote>\n<\/li>\n<li>The Photos_LoadCompleted method handles the LoadCompleted event to load all pages of the PhotoInfo feed returned by the data service.      \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">private void Photos_LoadCompleted(object sender, LoadCompletedEventArgs e)            <br \/>{             <br \/>&#160;&#160;&#160; if (e.Error == null)             <br \/>&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Make sure that we load all pages of the Customers feed.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (_photos.Continuation != null)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Request the next page from the data service.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _photos.LoadNextPartialSetAsync();             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; else             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ All pages are loaded.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IsDataLoaded = true;             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160; else             <br \/>&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (MessageBox.Show(e.Error.Message, &quot;Retry request?&quot;,             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MessageBoxButton.OKCancel) == MessageBoxResult.OK)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.LoadData();             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160; }             <br \/>}<\/font><\/p>\n<p><font size=\"2\" face=\"Courier New\"><\/font><\/p>\n<\/blockquote>\n<\/li>\n<li>When the user selects an image in the list, PhotoDetailsPage.xaml is navigated to, which displays data from the selected PhotoInfo object. <\/li>\n<\/ol>\n<h3>Binding Image Data to UI Controls<\/h3>\n<p>This sample displays images in the MainPage by binding a ListBox control to the Photos property of the ViewModel, which returns the binding collection containing data from the returned PhotoInfo feed. There are two ways to bind media resources from our streaming data service to the Image control. <\/p>\n<ul>\n<li>By defining an extension property on the media link entry. <\/li>\n<li>By implementing a value converter. <\/li>\n<\/ul>\n<p>Both of these approaches end up calling <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ee474324.aspx\">GetReadStreamUri<\/a> on the context to return the URI of the media resource a specific PhotoInfo object, which is called the read stream URI. We ended-up going with the extension property approach, which is rather simple and ends up looking like this:<\/p>\n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">public partial class PhotoInfo        <br \/>{         <br \/>&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Returns the media resource URI for binding.         <br \/>&#160;&#160;&#160;&#160;&#160;&#160; public Uri StreamUri         <br \/>&#160;&#160;&#160;&#160;&#160;&#160; {         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; get         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return App.ViewModel.GetReadStreamUri(this);         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }         <br \/>&#160;&#160;&#160;&#160;&#160;&#160; }         <br \/>}<\/font><\/p>\n<\/blockquote>\n<p>When you bind an Image control using the read stream URI, the runtime does the work of asynchronously downloading the media resource during binding. The following XAML shows this binding to the StreamUri extension property for the image source: <\/p>\n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">&lt;ListBox Margin=&quot;0,0,-12,0&quot; Name=&quot;PhotosListBox&quot; ItemsSource=&quot;{Binding Photos}&quot;&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; SelectionChanged=&quot;OnSelectionChanged&quot; Height=&quot;Auto&quot;&gt;         <br \/>&#160;&#160;&#160; &lt;ListBox.ItemsPanel&gt;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;ItemsPanelTemplate&gt;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;toolkit:WrapPanel ItemHeight=&quot;150&quot; ItemWidth=&quot;150&quot;\/&gt;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/ItemsPanelTemplate&gt;         <br \/>&#160;&#160;&#160; &lt;\/ListBox.ItemsPanel&gt;         <br \/>&#160;&#160;&#160; &lt;ListBox.ItemTemplate&gt;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;DataTemplate&gt;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;StackPanel Margin=&quot;0,0,0,17&quot; Orientation=&quot;Vertical&quot;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; HorizontalAlignment=&quot;Center&quot;&gt;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;Image Source=&quot;{Binding Path=StreamUri, Mode=OneWay}&quot;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Height=&quot;100&quot; Width=&quot;130&quot; \/&gt;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;TextBlock Text=&quot;{Binding Path=FileName, Mode=OneWay}&quot;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; HorizontalAlignment=&quot;Center&quot; Width=&quot;Auto&quot;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Style=&quot;{StaticResource PhoneTextNormalStyle}&quot;\/&gt;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/StackPanel&gt;         <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;\/DataTemplate&gt;         <br \/>&#160;&#160;&#160; &lt;\/ListBox.ItemTemplate&gt;         <br \/>&lt;\/ListBox&gt;<\/font><\/p>\n<\/blockquote>\n<p>Because the PhotoInfo class now includes the StreamUri extension property, the client also serializes this property in POST requests that create new media link entries in the data service. This causes an error in the data service when this unknown property cannot be processed. In our sample, we had to rewrite our requests to remove the StreamUri property from the request body. This payload rewriting is performed in the PhotoDataContainer partial class (defined in project file PhotoDataContainer.cs), which follows the basic pattern described in <a href=\"http:\/\/blogs.msdn.com\/b\/phaniraj\/archive\/2008\/12\/11\/customizing-serialization-of-entities-in-the-ado-net-data-services-client-library.aspx\">this post<\/a>.&#160; I cover this and other binding issues related to media resource streams in more detail in <a href=\"http:\/\/blogs.msdn.com\/b\/writingdata_services\/archive\/2011\/05\/09\/revisited-guidance-on-binding-media-resource-streams-to-xaml-controls.aspx\">my blog<\/a>. <\/p>\n<h3>Uploading a New Image to the Data Service <\/h3>\n<p>The following steps are required to create a new PhotoInfo entity and binary image file in the data service.<\/p>\n<ol>\n<li>When the user taps the CreatePhoto button to upload a new image, we must create a new MLE object on the client. We do this by calling <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ee474331\">DataServiceCollection<\/a>.<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms132404.aspx\">Add<\/a> in the MainPage<strong> <\/strong>code-behind page:       \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Create a new PhotoInfo instance.            <br \/>PhotoInfo newPhoto = PhotoInfo.CreatePhotoInfo(0, string.Empty,             <br \/>&#160;&#160;&#160; DateTime.Now, new Exposure(), new Dimensions(), DateTime.Now);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Add the new photo to the tracking collection.            <br \/>App.ViewModel.Photos.Add(newPhoto);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Select the newly added photo.            <br \/>this.PhotosListBox.SelectedItem = newPhoto;<\/font><\/p>\n<p><font size=\"2\" face=\"Courier New\"><\/font><\/p>\n<\/blockquote>\n<p> In this case, we don\u2019t need to call <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.data.services.client.dataservicecontext.addobject\">AddObject<\/a> on the context because we are using a <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ee474331\">DataServiceCollection<\/a> for data binding. <\/li>\n<li>When the new PhotoInfo is selected from the list, the following SelectionChanged handler is invoked:      \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">var selector = (Selector)sender;            <br \/>if (selector.SelectedIndex == -1)             <br \/>{             <br \/>&#160;&#160;&#160; return;             <br \/>}<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Navigate to the details page for the selected item.            <br \/>this.NavigationService.Navigate(             <br \/>&#160;&#160;&#160; new Uri(&quot;\/PhotoDetailsPage.xaml?selectedIndex=&quot;             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; + selector.SelectedIndex, UriKind.Relative));<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">selector.SelectedIndex = -1;<\/font><\/p>\n<\/blockquote>\n<p> This navigates to the PhotoDetailsPage with the index of the newly created PhotoInfo object in the query parameter. <\/li>\n<li>In the code-behind page for the PhotoDetailsPage, the following method handles the OnNavigatedTo event:&#160; \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">if (chooserCancelled == true)            <br \/>{             <br \/>&#160;&#160;&#160; \/\/ The user did not choose a photo so return to the main page;             <br \/>&#160;&#160;&#160; \/\/ the added PhotoInfo is already removed.             <br \/>&#160;&#160;&#160; NavigationService.GoBack();<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160; \/\/ Void out the binding so that we don&#8217;t try and bind            <br \/>&#160;&#160;&#160; \/\/ to an empty PhotoInfo object.             <br \/>&#160;&#160;&#160; this.DataContext = null;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160; return;            <br \/>}<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Get the selected PhotoInfo object.            <br \/>string indexAsString = this.NavigationContext.QueryString[&quot;selectedIndex&quot;];             <br \/>int index = int.Parse(indexAsString);             <br \/>this.DataContext = currentPhoto             <br \/>&#160;&#160;&#160; = (PhotoInfo)App.ViewModel.Photos[index];<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ If this is a new photo, we need to get the image file.            <br \/>if (currentPhoto.PhotoId == 0             <br \/>&#160;&#160;&#160; &amp;&amp; currentPhoto.FileName == string.Empty)             <br \/>{             <br \/>&#160;&#160;&#160; \/\/ Call the OnSelectPhoto method to open the chooser.             <br \/>&#160;&#160;&#160; this.OnSelectPhoto(this, new EventArgs());             <br \/>}<\/font><\/p>\n<\/blockquote>\n<p> If we have a new PhotoInfo object (with a zero ID), the OnSelectedPhoto method is called.&#160; <\/li>\n<li>In the PhotoDetailsPage, we must initialize the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.phone.tasks.photochoosertask(VS.92).aspx\">PhotoChooserTask<\/a> in the class constructor:&#160; \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Initialize the PhotoChooserTask and assign the Completed handler.            <br \/>photoChooser = new PhotoChooserTask();             <br \/>photoChooser.Completed +=             <br \/>&#160;&#160;&#160; new EventHandler&lt;PhotoResult&gt;(photoChooserTask_Completed);<\/font><\/p>\n<p><font size=\"2\" face=\"Courier New\"><\/font><\/p>\n<\/blockquote>\n<\/li>\n<li>In the OnSelectedPhoto method (which also handles the SelectPhoto button tap) we display the photo chooser:&#160; \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Start the PhotoChooser.            <br \/>photoChooser.Show();<\/font><\/p>\n<\/blockquote>\n<p> At this point, the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.phone.tasks.photochoosertask(VS.92).aspx\">PhotoChooserTask<\/a> is displayed and the application itself is deactivated, to be reactivated when the chooser closes\u2014hence the need to implement tombstoning.&#160; <\/li>\n<li>When the photo chooser is closed, the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ff707989(VS.92).aspx\">Completed<\/a> event is raised. When the application is fully reactivated, we handle the event as follows to set PhotoInfo properties based on the selected photo:       \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Get back the last PhotoInfo objcet in the collection,            <br \/>\/\/ which we just added.             <br \/>currentPhoto =             <br \/>&#160;&#160;&#160; App.ViewModel.Photos[App.ViewModel.Photos.Count &#8211; 1];<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">if (e.TaskResult == TaskResult.OK)            <br \/>{             <br \/>&#160;&#160;&#160; \/\/ Set the file properties for the returned image.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br \/>&#160;&#160;&#160; currentPhoto.FileName =             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; GetFileNameFromString(e.OriginalFileName);             <br \/>&#160;&#160;&#160; currentPhoto.ContentType =             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; GetContentTypeFromFileName(currentPhoto.FileName);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160; \/\/ Read remaining entity properties from the stream itself.            <br \/>&#160;&#160;&#160; currentPhoto.FileSize = (int)e.ChosenPhoto.Length;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160; \/\/ Create a new image using the returned memory stream.            <br \/>&#160;&#160;&#160; BitmapImage imageFromStream =             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; new System.Windows.Media.Imaging.BitmapImage();             <br \/>&#160;&#160;&#160; imageFromStream.SetSource(e.ChosenPhoto);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160; \/\/ Set the height and width of the image.            <br \/>&#160;&#160;&#160; currentPhoto.Dimensions.Height =             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; (short?)imageFromStream.PixelHeight;             <br \/>&#160;&#160;&#160; currentPhoto.Dimensions.Width =             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; (short?)imageFromStream.PixelWidth;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160; this.PhotoImage.Source = imageFromStream;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160; \/\/ Reset the stream before we pass it to the service.            <br \/>&#160;&#160;&#160; e.ChosenPhoto.Position = 0;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160; \/\/ Set the save stream for the selected photo stream.            <br \/>&#160;&#160;&#160; App.ViewModel.SetSaveStream(currentPhoto, e.ChosenPhoto, true,             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; currentPhoto.ContentType, currentPhoto.FileName);             <br \/>}             <br \/>else             <br \/>{             <br \/>&#160;&#160;&#160; \/\/ Assume that the select photo operation was cancelled,             <br \/>&#160;&#160;&#160; \/\/ remove the added PhotoInfo and navigate back to the main page.             <br \/>&#160;&#160;&#160; App.ViewModel.Photos.Remove(currentPhoto);             <br \/>&#160;&#160;&#160; chooserCancelled = true;             <br \/>}<\/font><\/p>\n<\/blockquote>\n<p> Note that we use the image stream to create a new BitmapImage, which is only used to automatically set the height and width properties of the image. <\/li>\n<li>When the Save button in the PhotoDetailsPage is tapped, we register a handler for the SaveChangesCompleted event in the ViewModel, start the progress bar, and call SaveChanges in the ViewModel:&#160; \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">App.ViewModel.SaveChangesCompleted +=            <br \/>&#160;&#160;&#160; new MainViewModel.SaveChangesCompletedEventHandler(ViewModel_SaveChangesCompleted);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">App.ViewModel.SaveChanges();<\/font><\/p>\n<p><font size=\"1\"><font face=\"Courier New\">\/\/ Show the progress bar during the request.              <br \/>this.requestProgress.Visibility = Visibility.Visible;               <br \/>this.requestProgress.IsIndeterminate = true;<\/font> <\/font><\/p>\n<\/blockquote>\n<\/li>\n<li>In the ViewModel, we call <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.data.services.client.dataservicecontext.savechanges\">BeginSaveChanges<\/a> to send the media resource as a binary stream (along with any other pending PhotoInfo object updates) to the data service:       \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Send an insert or update request to the data service.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br \/>this._context.BeginSaveChanges(OnSaveChangesCompleted, null);<\/font><\/p>\n<\/blockquote>\n<p> When <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.data.services.client.dataservicecontext.savechanges\">BeginSaveChanges<\/a> is called, the client sends a POST request to create the media resource in the data service using the supplied stream. After it processes the stream, the data service creates an empty media link entry. The client then sends a subsequent MERGE request to update this new PhotoInfo entity with data from the client. <\/li>\n<li>In the following callback method, we call the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.data.services.client.dataservicecontext.endsavechanges(VS.95).aspx\">EndSaveChanges<\/a> method to get the response to the POST request generated when <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.data.services.client.dataservicecontext.savechanges\">BeginSaveChanges<\/a> was called:       \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">private void OnSaveChangesCompleted(IAsyncResult result)            <br \/>{             <br \/>&#160;&#160;&#160; EntityDescriptor entity = null;             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br \/>&#160;&#160;&#160; \/\/ Use the Dispatcher to ensure that the response is             <br \/>&#160;&#160;&#160; \/\/ marshaled back to the UI thread.             <br \/>&#160;&#160;&#160; Deployment.Current.Dispatcher.BeginInvoke(() =&gt;             <br \/>&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; try             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Complete the save changes operation and display the response.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; DataServiceResponse response = _context.EndSaveChanges(result);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ When we issue a POST request, the photo ID and            <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ edit-media link are not updated on the client (a bug),             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ so we need to get the server values.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (response != null &amp;&amp; response.Count() &gt; 0)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var operation = response.FirstOrDefault()             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; as ChangeOperationResponse;             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; entity = operation.Descriptor as EntityDescriptor;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var changedPhoto = entity.Entity as PhotoInfo;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (changedPhoto != null &amp;&amp; changedPhoto.PhotoId == 0)            <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Verify that the entity was created correctly.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (entity != null &amp;&amp; entity.EditLink != null)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Detach the new entity from the context.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _context.Detach(entity.Entity);<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Get the updated entity from the service.            <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _context.BeginExecute&lt;PhotoInfo&gt;(entity.EditLink,             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; OnExecuteCompleted, null);             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; else             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Raise the SaveChangesCompleted event.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (SaveChangesCompleted != null)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; SaveChangesCompleted(this, new AsyncCompletedEventArgs());             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; catch (DataServiceRequestException ex)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Display the error from the response.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MessageBox.Show(ex.Message);             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; catch (InvalidOperationException ex)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MessageBox.Show(ex.GetBaseException().Message);             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160; });             <br \/>}<\/font><\/p>\n<p><font size=\"2\" face=\"Courier New\"><\/font><\/p>\n<\/blockquote>\n<p> When creating a new photo, we also need need to execute a query to get the newly created media link entry from the data service, after first detaching the new entity. We must do this because of a limitation in the WCF Data Services client POST behavior where it does not update the object on the client with the server-generated values or the edit-media link URI. To get the updated entity materialized correctly from the data service, we first detach the new entity and then call <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/cc646724(VS.95).aspx\">BeginExecute<\/a> to get the new media link entry. <\/li>\n<li>When we handle the callback from the subsequent query execution, we assign the returned object to a new instance to properly materialize the new media link entry:      \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">private void OnExecuteCompleted(IAsyncResult result)            <br \/>{             <br \/>&#160;&#160;&#160; \/\/ Use the Dispatcher to ensure that the response is             <br \/>&#160;&#160;&#160; \/\/ marshaled back to the UI thread.             <br \/>&#160;&#160;&#160; Deployment.Current.Dispatcher.BeginInvoke(() =&gt;             <br \/>&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; try             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Complete the query by assigning the returned             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ entity, which materializes the new instance             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ and attaches it to the context. We also need to assign the             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ new entity in the collection to the returned instance.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; PhotoInfo entity = _photos[_photos.Count &#8211; 1] =             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _context.EndExecute&lt;PhotoInfo&gt;(result).FirstOrDefault();<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Report that that media resource URI is updated.            <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; entity.ReportStreamUriUpdated();             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; catch (DataServiceQueryException ex)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; MessageBox.Show(ex.Message);             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; finally             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; \/\/ Raise the event by using the () operator.             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (SaveChangesCompleted != null)             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; SaveChangesCompleted(this, new AsyncCompletedEventArgs());             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }             <br \/>&#160;&#160;&#160; });             <br \/>}<\/font><\/p>\n<\/blockquote>\n<p> Because we detached the new media link entry, we must also assign the now tracked PhotoInfo object to the appropriate instance in the binding collection, otherwise the binding collection is out of sync with the context. <\/li>\n<li>Finally, the SaveChangesCompleted event is raised by the ViewModel, to inform the UI that it is OK to turn off the progress bar, which is handled in the following code in the PhotoDetailsPage:      \n<blockquote>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Hide the progress bar now that save changes operation is complete.            <br \/>this.requestProgress.Visibility = Visibility.Collapsed;             <br \/>this.requestProgress.IsIndeterminate = false;<\/font><\/p>\n<p><font size=\"1\" face=\"Courier New\">\/\/ Unregister for the SaveChangedCompleted event now that we are done.            <br \/>App.ViewModel.SaveChangesCompleted -=             <br \/>&#160;&#160;&#160; new MainViewModel.SaveChangesCompletedEventHandler(ViewModel_SaveChangesCompleted);             <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br \/>NavigationService.GoBack();<\/font><\/p>\n<\/blockquote>\n<p> Unfortunately, when the navigation returns to the MainPage, the binding again downloads the images. This is because of the application deactivation that occurs when the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.phone.tasks.photochoosertask(VS.92).aspx\">PhotoChooserTask<\/a> is displayed. To avoid this re-download from the data service after tombstoning, you could instead use the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ee474419.aspx\">GetReadStream<\/a> method to get a stream that contains the image data and use it to create an image in isolated storage. Then, your binding could access the stored version instead of the web version of the image, but this is outside the scope of this sample. <\/li>\n<\/ol>\n<p>&#160;<\/p>\n<h3>Glenn Gailey<\/h3>\n<p>Senior Programming Writer    <br \/>WCF Data Services<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this third post in the series on implementing a streaming data provider, we show how to use the OData client library for Windows Phone 7 to asynchronously access binary data exposed by an Open Data Protocol (OData) feed. We also show how to asynchronously upload binary data to the data service. This Windows Phone [&hellip;]<\/p>\n","protected":false},"author":512,"featured_media":3253,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[22,40,42,48,70,78,87,90],"class_list":["post-953","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-odata","tag-data-service","tag-idataservicestreamprovider","tag-media-resource","tag-odata","tag-streaming","tag-wcf-data-services","tag-windows-phone","tag-wp7"],"acf":[],"blog_post_summary":"<p>In this third post in the series on implementing a streaming data provider, we show how to use the OData client library for Windows Phone 7 to asynchronously access binary data exposed by an Open Data Protocol (OData) feed. We also show how to asynchronously upload binary data to the data service. This Windows Phone [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/953","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/users\/512"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/comments?post=953"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/953\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media\/3253"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media?parent=953"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/categories?post=953"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/tags?post=953"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}