{"id":653,"date":"2012-08-23T11:06:00","date_gmt":"2012-08-23T11:06:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/odatateam\/2012\/08\/23\/odata-101-building-our-first-odata-based-windows-store-app-part-2\/"},"modified":"2012-08-23T11:06:00","modified_gmt":"2012-08-23T11:06:00","slug":"odata-101-building-our-first-odata-based-windows-store-app-part-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/odata\/odata-101-building-our-first-odata-based-windows-store-app-part-2\/","title":{"rendered":"OData 101: Building our first OData-based Windows Store app (Part 2)"},"content":{"rendered":"<p><a href=\"https:\/\/www.bitwhys.com\/assets\/OData.WindowsStore.NetflixDemo.zip\">Download the sample code<\/a><\/p>\n<p>In the <a href=\"http:\/\/blogs.msdn.com\/b\/astoriateam\/archive\/2012\/08\/23\/odata-101-building-our-first-odata-based-windows-store-app-part-1.aspx\">previous blog post<\/a>, we walked through the steps to build an OData-enabled client using the new Windows UI. In this blog post, we&rsquo;ll take a look at some of the code that makes it happen.<\/p>\n<h3>ODataBindable, SampleDataItem and SampleDataGroup<\/h3>\n<p>In the walkthrough, we repurposed SampleDataSource.cs with some code from <a href=\"https:\/\/gist.github.com\/3419288\" target=\"_blank\">this gist<\/a>. In that gist, ODataBindable, SampleDataItem and SampleDataGroup were all stock classes from the project template (ODataBindable was renamed from SampleDataCommon, but otherwise the classes are exactly the same).<\/p>\n<h3>ExtensionMethods<\/h3>\n<p>The extension methods class contains two simple extension methods. Each of these extension methods uses the <a href=\"http:\/\/download.microsoft.com\/download\/5\/B\/9\/5B924336-AA5D-4903-95A0-56C6336E32C9\/TAP.docx\" target=\"_blank\">Task-based Asynchronous Pattern<\/a> (TAP) to allow the <strong>SampleDataSource<\/strong> to execute an OData query without blocking the UI.<\/p>\n<p>For instance, the following code uses the very handy <strong>Task.Factory.FromAsync<\/strong> method to implement TAP:<\/p>\n<pre class=\"prettyprint linenums\">public static async Task&lt;IEnumerable&lt;T&gt;&gt; ExecuteAsync&lt;T&gt;(this DataServiceQuery&lt;T&gt; query)\n{\n    return await Task.Factory.FromAsync&lt;IEnumerable&lt;T&gt;&gt;(query.BeginExecute(null, null), query.EndExecute);\n}<\/pre>\n<h3>SampleDataSource<\/h3>\n<p>The <strong>SampleDataSource<\/strong> class has a significant amount of overlap with the stock implementation. The changes I made were to bring it just a bit closer to the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Singleton_pattern\" target=\"_blank\">Singleton pattern<\/a> and the implementation of two important methods.<\/p>\n<h4>Search<\/h4>\n<p>The <strong>Search<\/strong> method is an extremely simplistic implementation of search. In this case it literally just does an in-memory search of the loaded movies. It is very easy to imagine passing the search term through to a <strong>.Where()<\/strong> clause, and I encourage you to do so in your own implementation. In this case I was trying to keep the code as simple as possible.<\/p>\n<pre class=\"prettyprint linenums\">public static IEnumerable&lt;SampleDataItem&gt; Search(string searchString)\n{\n\tvar regex = new Regex(searchString, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);\n\treturn Instance.AllGroups\n\t    .SelectMany(g =&gt; g.Items)\n\t    .Where(m =&gt; regex.IsMatch(m.Title) || regex.IsMatch(m.Subtitle))\n\t\t.Distinct(new SampleDataItemComparer());\n}<\/pre>\n<p><strong>LoadMovies<\/strong><\/p>\n<p>The <strong>LoadMovies<\/strong> method is where the more interesting code exists.<\/p>\n<pre class=\"prettyprint linenums\">public static async void LoadMovies()\n{\n    IEnumerable&lt;Title&gt; titles = await ((DataServiceQuery&lt;Title&gt;)Context.Titles\n        .Expand(\"Genres,AudioFormats,AudioFormats\/Language,Awards,Cast\")\n        .Where(t =&gt; t.Rating == \"PG\")\n        .OrderByDescending(t =&gt; t.ReleaseYear)\n        .Take(300)).ExecuteAsync();\n\n    foreach (Title title in titles)\n    {\n        foreach (Genre netflixGenre in title.Genres)\n        {\n            SampleDataGroup genre = GetGroup(netflixGenre.Name);\n            if (genre == null)\n            {\n                genre = new SampleDataGroup(netflixGenre.Name, netflixGenre.Name, String.Empty, title.BoxArt.LargeUrl, String.Empty);\n                Instance.AllGroups.Add(genre);\n            }\n            var content = new StringBuilder();\n            \/\/ Write additional things to content here if you want them to display in the item detail.\n            genre.Items.Add(new SampleDataItem(title.Id, title.Name, String.Format(\"{0}\\r\\n\\r\\n{1} ({2})\", title.Synopsis, title.Rating, title.ReleaseYear), title.BoxArt.HighDefinitionUrl ?? title.BoxArt.LargeUrl, \"Description\", content.ToString()));\n        }\n    }\n}<\/pre>\n<p>The first and most interesting thing we do is to use the TAP pattern again to asynchronously get 300 (<strong>Take<\/strong>) recent (<strong>OrderByDescending<\/strong>) PG-rated (<strong>Where<\/strong>) movies back from Netflix. The rest of the code is simply constructing <strong>SimpleDataItem<\/strong>s and <strong>SimpleDataGroup<\/strong>s from the entities that were returned in the OData feed.<\/p>\n<h3>SearchResultsPage<\/h3>\n<p>Finally, we have just a bit of calling code in <strong>SearchResultsPage<\/strong>. When a user searches from the <strong>Win+F<\/strong> experience, the <strong>LoadState<\/strong> method is called first, enabling us to intercept what was searched for. In our case, the stock implementation is okay aside from the fact that we don&rsquo;t any additional quotes embedded, so we&rsquo;ll modify the line that puts the value into the <strong>DefaultViewModel<\/strong> to not append quotes:<\/p>\n<pre class=\"prettyprint linenums\">this.DefaultViewModel[\"QueryText\"] = queryText;<\/pre>\n<p>When the filter actually changes, we want to pass the call through to our implementation of search, which we can do with the stock implementation of <strong>Filter_SelectionChanged<\/strong>:<\/p>\n<pre class=\"prettyprint linenums\">void Filter_SelectionChanged(object sender, SelectionChangedEventArgs e)\n{\n    \/\/ Determine what filter was selected\n    var selectedFilter = e.AddedItems.FirstOrDefault() as Filter;\n    if (selectedFilter != null)\n    {\n        \/\/ Mirror the results into the corresponding Filter object to allow the\n        \/\/ RadioButton representation used when not snapped to reflect the change\n        selectedFilter.Active = true;\n\n        \/\/ TODO: Respond to the change in active filter by setting this.DefaultViewModel[\"Results\"]\n        \/\/       to a collection of items with bindable Image, Title, Subtitle, and Description properties\n        var searchValue = (string)this.DefaultViewModel[\"QueryText\"];\n        this.DefaultViewModel[\"Results\"] = new List&lt;SampleDataItem&gt;(SampleDataSource.Search(searchValue));\n\n        \/\/ Ensure results are found\n        object results;\n        ICollection resultsCollection;\n        if (this.DefaultViewModel.TryGetValue(\"Results\", out results) &amp;&amp;\n            (resultsCollection = results as ICollection) != null &amp;&amp;\n            resultsCollection.Count != 0)\n        {\n            VisualStateManager.GoToState(this, \"ResultsFound\", true);\n            return;\n        }\n    }\n\n    \/\/ Display informational text when there are no search results.\n    VisualStateManager.GoToState(this, \"NoResultsFound\", true);\n}<\/pre>\n<h4>Item_Clicked<\/h4>\n<p>Optionally, you can implement an event handler that will cause the page to navigate to the selected item by copying similar code from <strong>GroupedItemsPage.xaml.cs<\/strong>. The event binding will also need to be added to the <strong>resultsGridView<\/strong> in XAML. You can see this code in the published sample.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Download the sample code In the previous blog post, we walked through the steps to build an OData-enabled client using the new Windows UI. In this blog post, we&rsquo;ll take a look at some of the code that makes it happen. ODataBindable, SampleDataItem and SampleDataGroup In the walkthrough, we repurposed SampleDataSource.cs with some code from [&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":[48,65,86],"class_list":["post-653","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-odata","tag-odata","tag-samples","tag-windows-8"],"acf":[],"blog_post_summary":"<p>Download the sample code In the previous blog post, we walked through the steps to build an OData-enabled client using the new Windows UI. In this blog post, we&rsquo;ll take a look at some of the code that makes it happen. ODataBindable, SampleDataItem and SampleDataGroup In the walkthrough, we repurposed SampleDataSource.cs with some code from [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/653","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=653"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/653\/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=653"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/categories?post=653"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/tags?post=653"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}