public
Created

Sample Windows Store app with OData consumption

  • Download Gist
SampleDataSource.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Data.Services.Client;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using OData.WindowsStore.NetflixDemo.Common;
using OData.WindowsStore.NetflixDemo.Netflix;
using Windows.Foundation.Metadata;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
 
namespace OData.WindowsStore.NetflixDemo.Data
{
public class SampleDataSource
{
private static readonly SampleDataSource Instance = new SampleDataSource();
 
private static readonly NetflixCatalog Context = new NetflixCatalog(new Uri("http://odata.netflix.com/Catalog"));
private readonly ObservableCollection<SampleDataGroup> allGroups = new ObservableCollection<SampleDataGroup>();
 
static SampleDataSource()
{
LoadMovies();
}
 
public ObservableCollection<SampleDataGroup> AllGroups
{
get { return allGroups; }
}
 
public static IEnumerable<SampleDataItem> Search(string searchString)
{
var regex = new Regex(searchString,
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase |
RegexOptions.IgnorePatternWhitespace);
return
Instance.AllGroups.SelectMany(g => g.Items).Where(
m => regex.IsMatch(m.Title) || regex.IsMatch(m.Subtitle)).Distinct(new SampleDataItemComparer());
}
 
public static IEnumerable<SampleDataGroup> GetGroups(string uniqueId)
{
if (!uniqueId.Equals("AllGroups"))
throw new ArgumentException("Only 'AllGroups' is supported as a collection of groups");
 
return Instance.AllGroups;
}
 
public static SampleDataGroup GetGroup(string id)
{
IEnumerable<SampleDataGroup> matches =
Instance.AllGroups.Where(group => group.UniqueId.Equals(id));
return matches.FirstOrDefault();
}
 
public static SampleDataItem GetItem(string id)
{
IEnumerable<SampleDataItem> matches =
Instance.AllGroups.SelectMany(group => group.Items).Where(item => item.UniqueId.Equals(id));
return matches.FirstOrDefault();
}
 
/// <summary>
/// Loads movies asynchronously onto the singleton.
/// </summary>
public static async void LoadMovies()
{
IEnumerable<Title> titles =
await
((DataServiceQuery<Title>)
Context.Titles.Expand("Genres,AudioFormats,AudioFormats/Language,Awards,Cast").Where(
t => t.Rating == "PG").OrderByDescending(t => t.ReleaseYear).Take(300)).ExecuteAsync();
 
foreach (Title title in titles)
{
foreach (Genre netflixGenre in title.Genres)
{
SampleDataGroup genre = GetGroup(netflixGenre.Name);
if (genre == null)
{
genre = new SampleDataGroup(netflixGenre.Name, netflixGenre.Name, String.Empty,
title.BoxArt.LargeUrl,
String.Empty);
Instance.AllGroups.Add(genre);
}
var content = new StringBuilder();
// Write additional things to content here if you want them to display in the item detail.
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()));
}
}
}
}
 
[WebHostHidden]
public abstract class ODataBindable : BindableBase
{
private static readonly Uri BaseUri = new Uri("ms-appx:///");
private string description = string.Empty;
private ImageSource image;
private String imagePath;
private string subtitle = string.Empty;
private string title = string.Empty;
 
private string uniqueId = string.Empty;
 
protected ODataBindable(String uniqueId, String title, String subtitle, String imagePath, String description)
{
this.uniqueId = uniqueId;
this.title = title;
this.subtitle = subtitle;
this.description = description;
this.imagePath = imagePath;
}
 
public string UniqueId
{
get { return uniqueId; }
set { SetProperty(ref uniqueId, value); }
}
 
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
 
public string Subtitle
{
get { return subtitle; }
set { SetProperty(ref subtitle, value); }
}
 
public string Description
{
get { return description; }
set { SetProperty(ref description, value); }
}
 
public ImageSource Image
{
get
{
if (image == null && imagePath != null)
{
image = new BitmapImage(new Uri(BaseUri, imagePath));
}
return image;
}
 
set
{
imagePath = null;
SetProperty(ref image, value);
}
}
 
public void SetImage(String path)
{
image = null;
imagePath = path;
OnPropertyChanged("Image");
}
 
public override string ToString()
{
return Title;
}
}
 
/// <summary>
/// Functionally represents a genre in the Netflix catalog. Name and functionality
/// left as-is to minimize changes to XAML bindings.
/// </summary>
public class SampleDataGroup : ODataBindable
{
private readonly ObservableCollection<SampleDataItem> items = new ObservableCollection<SampleDataItem>();
 
private readonly ObservableCollection<SampleDataItem> topItem = new ObservableCollection<SampleDataItem>();
 
public SampleDataGroup(String uniqueId, String title, String subtitle, String imagePath, String description)
: base(uniqueId, title, subtitle, imagePath, description)
{
Items.CollectionChanged += ItemsCollectionChanged;
}
 
public ObservableCollection<SampleDataItem> Items
{
get { return items; }
}
 
public ObservableCollection<SampleDataItem> TopItems
{
get { return topItem; }
}
 
private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Provides a subset of the full items collection to bind to from a GroupedItemsPage
// for two reasons: GridView will not virtualize large items collections, and it
// improves the user experience when browsing through groups with large numbers of
// items.
//
// A maximum of 12 items are displayed because it results in filled grid columns
// whether there are 1, 2, 3, 4, or 6 rows displayed
 
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (SampleDataItem movie in e.NewItems)
{
movie.Group = this;
}
if (e.NewStartingIndex < 12)
{
TopItems.Insert(e.NewStartingIndex, Items[e.NewStartingIndex]);
if (TopItems.Count > 12)
{
TopItems.RemoveAt(12);
}
}
break;
case NotifyCollectionChangedAction.Move:
if (e.OldStartingIndex < 12 && e.NewStartingIndex < 12)
{
TopItems.Move(e.OldStartingIndex, e.NewStartingIndex);
}
else if (e.OldStartingIndex < 12)
{
TopItems.RemoveAt(e.OldStartingIndex);
TopItems.Add(Items[11]);
}
else if (e.NewStartingIndex < 12)
{
TopItems.Insert(e.NewStartingIndex, Items[e.NewStartingIndex]);
TopItems.RemoveAt(12);
}
break;
case NotifyCollectionChangedAction.Remove:
if (e.OldStartingIndex < 12)
{
TopItems.RemoveAt(e.OldStartingIndex);
if (Items.Count >= 12)
{
TopItems.Add(Items[11]);
}
}
break;
case NotifyCollectionChangedAction.Replace:
if (e.OldStartingIndex < 12)
{
TopItems[e.OldStartingIndex] = Items[e.OldStartingIndex];
}
break;
case NotifyCollectionChangedAction.Reset:
TopItems.Clear();
while (TopItems.Count < Items.Count && TopItems.Count < 12)
{
TopItems.Add(Items[TopItems.Count]);
}
break;
}
}
}
 
/// <summary>
/// Functionally represents a movie in the Netflix catalog. Name and functionality
/// left as-is to minimize changes to XAML bindings.
/// </summary>
public class SampleDataItem : ODataBindable
{
private string content = string.Empty;
 
private SampleDataGroup group;
 
public SampleDataItem(String uniqueId, String title, String subtitle, String imagePath, String description,
String content)
: base(uniqueId, title, subtitle, imagePath, description)
{
this.content = content;
}
 
public string Content
{
get { return content; }
set { SetProperty(ref content, value); }
}
 
public SampleDataGroup Group
{
get { return group; }
set { SetProperty(ref group, value); }
}
}
 
/// <summary>
/// Allows us to get a distinct set of search results.
/// </summary>
public class SampleDataItemComparer : IEqualityComparer<SampleDataItem>
{
#region IEqualityComparer<SampleDataItem> Members
 
public bool Equals(SampleDataItem x, SampleDataItem y)
{
return String.Equals(x.UniqueId, y.UniqueId, StringComparison.Ordinal);
}
 
public int GetHashCode(SampleDataItem obj)
{
return obj.UniqueId.GetHashCode();
}
 
#endregion
}
 
public static class ExtensionMethods
{
public static async Task<IEnumerable<T>> ExecuteAsync<T>(this DataServiceQuery<T> query)
{
return await Task.Factory.FromAsync<IEnumerable<T>>(query.BeginExecute(null, null), query.EndExecute);
}
 
public static async Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceContext context,
Uri requestUri)
{
return await Task.Factory.FromAsync<IEnumerable<TResult>>(context.BeginExecute<TResult>(requestUri, null, null),
executeAsyncResult =>
{
List<TResult> executeResult =
context.EndExecute<TResult>(executeAsyncResult)
.ToList();
return executeResult;
});
}
}
}
SearchResultsPage.xaml.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OData.WindowsStore.NetflixDemo.Data;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
 
// The Search Contract item template is documented at http://go.microsoft.com/fwlink/?LinkId=234240
 
namespace OData.WindowsStore.NetflixDemo
{
/// <summary>
/// This page displays search results when a global search is directed to this application.
/// </summary>
public sealed partial class SearchResultsPage : OData.WindowsStore.NetflixDemo.Common.LayoutAwarePage
{
 
public SearchResultsPage()
{
this.InitializeComponent();
}
 
/// <summary>
/// Populates the page with content passed during navigation. Any saved state is also
/// provided when recreating a page from a prior session.
/// </summary>
/// <param name="navigationParameter">The parameter value passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
/// </param>
/// <param name="pageState">A dictionary of state preserved by this page during an earlier
/// session. This will be null the first time a page is visited.</param>
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
var queryText = navigationParameter as String;
 
// TODO: Application-specific searching logic. The search process is responsible for
// creating a list of user-selectable result categories:
//
// filterList.Add(new Filter("<filter name>", <result count>));
//
// Only the first filter, typically "All", should pass true as a third argument in
// order to start in an active state. Results for the active filter are provided
// in Filter_SelectionChanged below.
 
var filterList = new List<Filter>();
filterList.Add(new Filter("All", 0, true));
 
// Communicate results through the view model
this.DefaultViewModel["QueryText"] = queryText;
this.DefaultViewModel["Filters"] = filterList;
this.DefaultViewModel["ShowFilters"] = filterList.Count > 1;
}
 
/// <summary>
/// Invoked when a filter is selected using the ComboBox in snapped view state.
/// </summary>
/// <param name="sender">The ComboBox instance.</param>
/// <param name="e">Event data describing how the selected filter was changed.</param>
void Filter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Determine what filter was selected
var selectedFilter = e.AddedItems.FirstOrDefault() as Filter;
if (selectedFilter != null)
{
// Mirror the results into the corresponding Filter object to allow the
// RadioButton representation used when not snapped to reflect the change
selectedFilter.Active = true;
 
// TODO: Respond to the change in active filter by setting this.DefaultViewModel["Results"]
// to a collection of items with bindable Image, Title, Subtitle, and Description properties
var searchValue = (string)this.DefaultViewModel["QueryText"];
this.DefaultViewModel["Results"] = new List<SampleDataItem>(SampleDataSource.Search(searchValue));
 
// Ensure results are found
object results;
ICollection resultsCollection;
if (this.DefaultViewModel.TryGetValue("Results", out results) &&
(resultsCollection = results as ICollection) != null &&
resultsCollection.Count != 0)
{
VisualStateManager.GoToState(this, "ResultsFound", true);
return;
}
}
 
// Display informational text when there are no search results.
VisualStateManager.GoToState(this, "NoResultsFound", true);
}
 
/// <summary>
/// Invoked when a filter is selected using a RadioButton when not snapped.
/// </summary>
/// <param name="sender">The selected RadioButton instance.</param>
/// <param name="e">Event data describing how the RadioButton was selected.</param>
void Filter_Checked(object sender, RoutedEventArgs e)
{
// Mirror the change into the CollectionViewSource used by the corresponding ComboBox
// to ensure that the change is reflected when snapped
if (filtersViewSource.View != null)
{
var filter = (sender as FrameworkElement).DataContext;
filtersViewSource.View.MoveCurrentTo(filter);
}
}
 
/// <summary>
/// View model describing one of the filters available for viewing search results.
/// </summary>
private sealed class Filter : OData.WindowsStore.NetflixDemo.Common.BindableBase
{
private String _name;
private int _count;
private bool _active;
 
public Filter(String name, int count, bool active = false)
{
this.Name = name;
this.Count = count;
this.Active = active;
}
 
public override String ToString()
{
return Description;
}
 
public String Name
{
get { return _name; }
set { if (this.SetProperty(ref _name, value)) this.OnPropertyChanged("Description"); }
}
 
public int Count
{
get { return _count; }
set { if (this.SetProperty(ref _count, value)) this.OnPropertyChanged("Description"); }
}
 
public bool Active
{
get { return _active; }
set { this.SetProperty(ref _active, value); }
}
 
public String Description
{
get { return String.Format("{0} ({1})", _name, _count); }
}
}
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.