|
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
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;
using C1.Xaml.Tile;
using System.Reflection;
using System.Collections.ObjectModel;
using GridViewSampleC1Tiles.DataModel;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace GridViewSampleC1Tiles
{
/// <summary>
/// This sample shows customized GridViewEx control bound to the grouped data source.
/// Drag&drop is implemented in the GridViewEx control.
/// It also uses C1Tile and C1TileService to mimic the Windows 8 Start screen behavior with animated tiles.
/// The page state is persisted at navigating from this page.
/// The SaveState method performs the simplest serialization
/// of the whole _groups collection to the JSON string and adds it to the page state.
/// The LoadState method performs deserialization of the previously saved groups or creates groups from scratch if page state is empty.
/// GridViewSampleC1Tiles.App class serializes this state along with application state using GridViewSampleC1Tiles.Common.SuspensionManager
/// class. For more details see GridViewSampleC1Tiles.App code and code comments.
/// Note, depending on your data structure and size you can implement persistence in the other way or save your data to some other place.
/// If you need more information about saving the app's state, start from this article:
/// http://msdn.microsoft.com/en-us/library/windows/apps/hh986968.aspx.
/// </summary>
public sealed partial class MainPage : GridViewSampleC1Tiles.Common.LayoutAwarePage
{
List<Group> _groups = new List<Group>();
public MainPage()
{
C1TileService.UpdateInterval = TimeSpan.FromSeconds(2); // make update interval smaller as there are too many items
C1TileService.MaxAnimationNumber = 3; // allow up to 3 animations at the same time
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)
{
base.LoadState(navigationParameter, pageState);
if (pageState != null && pageState.Count > 0 && pageState.ContainsKey("Groups"))
{
// restore groups and items from the previously serialized state
System.Runtime.Serialization.Json.DataContractJsonSerializer rootSer =
new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(List<Group>));
var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes((string)pageState["Groups"]));
_groups = (List<Group>)rootSer.ReadObject(stream);
}
else
{
// if we get here for the first time and don't have serialized content, fill groups and items from scratch
for (int j = 1; j <= 12; j++)
{
Group group = Group.GetNewGroup();
for (int i = 1; i <= 7 + j % 3; i++)
{
group.Items.Add(new Item()
{
Id = i,
GroupId = group.Id
});
}
_groups.Add(group);
}
}
UpdateDataContext();
}
/// <summary>
/// Preserves state associated with this page in case the application is suspended or the
/// page is discarded from the navigation cache. Values must conform to the serialization
/// requirements of <see cref="SuspensionManager.SessionState"/>.
/// </summary>
/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
protected override void SaveState(Dictionary<String, Object> pageState)
{
// save groups and items to JSON string so that it's possible to restore page state later
base.SaveState(pageState);
System.Runtime.Serialization.Json.DataContractJsonSerializer rootSer =
new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(List<Group>));
var stream = new MemoryStream();
rootSer.WriteObject(stream, _groups);
string str = System.Text.Encoding.UTF8.GetString(stream.ToArray(), 0, (int)stream.Length);
pageState.Add("Groups", str);
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// restore page state
var frameState = GridViewSampleC1Tiles.Common.SuspensionManager.SessionStateForFrame(this.Frame);
if (frameState.ContainsKey("TilePageData"))
{
this.LoadState(e.Parameter, (Dictionary<String, Object>)frameState["TilePageData"]);
}
else
{
this.LoadState(e.Parameter, null);
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// save page state with "TilePageData" key
var frameState = GridViewSampleC1Tiles.Common.SuspensionManager.SessionStateForFrame(this.Frame);
var pageState = new Dictionary<String, Object>();
this.SaveState(pageState);
frameState["TilePageData"] = pageState;
}
/// <summary>
/// Creates new CollectionViewSource and updates page DataContext.
/// </summary>
private void UpdateDataContext()
{
CollectionViewSource source = new CollectionViewSource();
source.Source = _groups;
source.ItemsPath = new PropertyPath("Items");
source.IsSourceGrouped = true;
this.DataContext = source;
}
// creates new group in the data source, if end-user drags item to the new group placeholder
private void MyGridView_BeforeDrop(object sender, Controls.BeforeDropItemsEventArgs e)
{
if (e.RequestCreateNewGroup)
{
// create new group and re-assign datasource
Group group = Group.GetNewGroup();
_groups.Insert(e.NewGroupIndex, group);
UpdateDataContext();
}
}
// removes empty groups (except the last one)
private void MyGridView_Drop(object sender, DragEventArgs e)
{
bool needReset = false;
for (int i = _groups.Count - 1; i >= 0; i--)
{
if (_groups[i].Items.Count == 0 && _groups.Count > 1)
{
_groups.RemoveAt(i);
needReset = true;
}
}
if (needReset)
{
UpdateDataContext();
}
}
}
/// <summary>
/// This class uses some additional code for correct working with C1 tiles in the GridView.ItemTemplate.
/// It also sets VariableSizedWrapGrid.ColumnSpanProperty for GridViewItem controls, so that every item can have different size in the VariableSizedWrapGrid.
/// </summary>
public class TileGridView : GridViewSampleC1Tiles.Controls.GridViewEx
{
protected override DependencyObject GetContainerForItemOverride()
{
// Use MyGridViewItem to avoid issues when we need both C1TileService.PointerDownAnimation and GridView dragging.
return new TileGridViewItem();
}
// set ColumnSpan according to the business logic (maybe some GridViewSamples.Samples.Item or group properties)
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
Item it = item as Item;
int colSpan = 1;
try
{
if (it != null)
{
colSpan = it.GroupId % 2 + 1;
element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, colSpan);
}
}
catch
{
element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, 1);
}
finally
{
base.PrepareContainerForItemOverride(element, item);
}
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
// when TileGridViewItem visual tree is created, find the C1Tile control and apply additional settings
IList<DependencyObject> list = new List<DependencyObject>();
C1.Xaml.VTreeHelper.GetChildrenOfType(element, typeof(C1TileBase), ref list);
foreach (C1TileBase tile in list)
{
// unfreeze tile after changing tile content
tile.IsFrozen = false;
// some tile animations might require explicit width setting, so set it according to the column span
tile.Width = colSpan == 1 ? 150 : 310;
}
});
}
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
// to avoid memory leaks, freeze tile before removing
IList<DependencyObject> list = new List<DependencyObject>();
C1.Xaml.VTreeHelper.GetChildrenOfType(element, typeof(C1TileBase), ref list);
foreach (C1TileBase tile in list)
{
tile.IsFrozen = true;
}
base.ClearContainerForItemOverride(element, item);
}
}
/// <summary>
/// Allows to select data template depending on some custom logic.
/// </summary>
public class TileGridViewItemTemplateSelector : C1.Xaml.C1DataTemplateSelector
{
static Random randomizer = new Random();
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
int templateNumber = randomizer.Next(3);
Item sampleItem = item as Item;
if ((sampleItem.GroupId + sampleItem.Id) % 10 == 3)
{
templateNumber = 3;
}
return Resources["ItemTemplate" + templateNumber.ToString()] as DataTemplate;
}
}
/// <summary>
/// Use the MyGridViewItem class if you need both C1TileService.PointerDownAnimation and GridView dragging functionality.
/// This class handles pointer captures so that there are no any conflicts between C1TileService and GridView actions.
/// </summary>
public class TileGridViewItem : GridViewItem
{
protected override void OnPointerCaptureLost(PointerRoutedEventArgs e)
{
C1TileService.SetPreservePointerCapture(this, false);
base.OnPointerCaptureLost(e);
}
protected override void OnPointerReleased(PointerRoutedEventArgs e)
{
C1TileService.SetPreservePointerCapture(this, false);
base.OnPointerReleased(e);
}
protected override void OnPointerMoved(PointerRoutedEventArgs e)
{
if (e.Pointer.IsInContact)
{
// Tell C1TileService to not release pointer capture at moving mouse, as it might be required for dragging.
// GridView will release pointer capture when it's done, or it will be done at PointerReleased or PointerCaptureLost.
C1TileService.SetPreservePointerCapture(this, true);
C1TileService.HandlePointerMoved(this, e);
}
base.OnPointerMoved(e);
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I am the ComponentOne product manager at GrapeCity. I love .NET but especially the XAML platforms. You'll find me blogging about these awesome technologies and at various code camps, techfests and tradeshows.