Creating a Hub Experience in a Windows Store Application
Join the DZone community and get the full member experience.
Join For Freeas i was developing the microsoft wowzapp application for windows 8, one of the responsibilities i had was creating the core dashboard, that displayed the event list, as well some information about event resources and participants. normally, this is easily done with the help of a simple gridview however, in this case the items were grouped in a very specific manner, and were variable in size. by default the gridview control does not let me group items consistently in this way, therefore i had to resort to using a modified control - variablegridview , initially documented by jerry nixon .
there is no xaml representation for this control, but rather a simple c#-based implementation that still relies on the gridview, overriding the preparecontainerforitemoverride method:
using windows.ui.xaml.controls; using wowzapp.models; namespace wowzapp.controls { public class variablegridview : gridview { protected override void preparecontainerforitemoverride(windows.ui.xaml.dependencyobject element, object item) { try { genericdashitem _item = (genericdashitem)item; element.setvalue(windows.ui.xaml.controls.variablesizedwrapgrid.columnspanproperty, _item.columnspan); element.setvalue(windows.ui.xaml.controls.variablesizedwrapgrid.rowspanproperty, _item.rowspan); } catch { element.setvalue(windows.ui.xaml.controls.variablesizedwrapgrid.columnspanproperty, 1); element.setvalue(windows.ui.xaml.controls.variablesizedwrapgrid.rowspanproperty, 1); } finally { base.preparecontainerforitemoverride(element, item); } } } }it relies on a collection of grouped items. in my case, a single dashgroup item was represented by the dashitem class:
using system; using system.collections.generic; using system.collections.objectmodel; using system.linq; using system.text; using system.threading.tasks; using wowzapp.common; namespace wowzapp.models { public class dashgroup : bindablebase { public dashgroup(dashgrouptype dashtype, string title) { dashtype = dashtype; title = title; items = new observablecollection<genericdashitem>(); } private dashgrouptype _dashtype; public dashgrouptype dashtype { get { return this._dashtype; } set { this.setproperty(ref this._dashtype, value); } } private string _title; public string title { get { return this._title; } set { this.setproperty(ref this._title, value); } } private observablecollection<genericdashitem> _items = new observablecollection<genericdashitem>(); public observablecollection<genericdashitem> items { get { return this._items; } set { this.setproperty(ref this._items, value); } } } }
it is the foundation of the grouped hub. its items property is automatically set to contain the dash group children, in this case - genericdashitem .
using system; using system.collections.generic; using system.linq; using system.text; using system.threading.tasks; using wowzapp.common; namespace wowzapp.models { public class genericdashitem : bindablebase { public genericdashitem(string uniqueid, string title, string subtitle, string description, uri imageurl) { uniqueid = uniqueid; title = title; subtitle = subtitle; description = description; image = imageurl; } private string _uniqueid; public string uniqueid { get { return this._uniqueid; } set { this.setproperty(ref this._uniqueid, value); } } private string _title; public string title { get { return this._title; } set { this.setproperty(ref this._title, value); } } private string _tag; public string tag { get { return this._tag; } set { this.setproperty(ref this._tag, value); } } private string _subtitle; public string subtitle { get { return this._subtitle; } set { this.setproperty(ref this._subtitle, value); } } private string _description; public string description { get { return this._description; } set { this.setproperty(ref this._description, value); } } private int _columnspan; public int columnspan { get { return this._columnspan; } set { this.setproperty(ref this._columnspan, value); } } private int _rowspan; public int rowspan { get { return this._rowspan; } set { this.setproperty(ref this._rowspan, value); } } private uri _imageurl; public uri image { get { return this._imageurl; } set { this.setproperty(ref this._imageurl, value); } } } }
obviously, you can modify the set of properties you want to bind to, as there are no restrictions on the type or number of those. as there is a limited set of parameters that i need to actually display to the user, i kept the basic title/subtitle/image set, and added a tag property, that will be used to carry any potential non-visible information.
notice that both the row span ( rowspan ) and the column span ( columnspan ) are item-specific and are not set through a template selector or a similar helper.
internally, you can create a dashgroup, add as many items as necessary and then pass those to an observablecollection that is bound to the custom gridview:
public static void initializeprofilepane() { dashgroup group1 = new dashgroup(dashgrouptype.profile, "me"); genericdashitem item4 = new genericdashitem("dashitem2", "my event", "subtitle", "description", new uri("http://www.microsoft.com/global/en-us/news/publishingimages/homepage/hero/spot_toyota365_hero.jpg", urikind.absolute)); item4.columnspan = 1; item4.rowspan = 1; group1.items.add(item4); genericdashitem item3 = new genericdashitem("dashitem2", "my app", "subtitle", "description", new uri("http://www.microsoft.com/global/en-us/news/publishingimages/homepage/hero/spot_toyota365_hero.jpg", urikind.absolute)); item3.columnspan = 1; item3.rowspan = 1; group1.items.add(item3); genericdashitem item5 = new genericdashitem("dashitem2", "inbox", "subtitle", "description", new uri("http://www.microsoft.com/global/en-us/news/publishingimages/homepage/hero/spot_toyota365_hero.jpg", urikind.absolute)); item5.columnspan = 1; item5.rowspan = 1; group1.items.add(item5); coreviewmodel.instance.dashboardgroups.add(group1); }
when binding, make sure that you proxy the collection through a collectionviewsource:
<collectionviewsource x:name="groupedsource" source="{binding source={staticresource cvm},path=instance.dashboardgroups}" issourcegrouped="true" itemspath="items"/>
issourcegrouped will be the primary flag that indicates that the grouping was already done. the grouped view will display the groups with correct binding, given the following configuration:
<controls:variablegridview x:name="itemgridview" padding="116,46,40,46" grid.row="1" itemssource="{binding source={staticresource groupedsource}}" selectionmode="none" isswipeenabled="false" isitemclickenabled="true" itemclick="itemgridview_itemclick_1"> <gridview.groupstyle> <groupstyle> <groupstyle.headertemplate> <datatemplate> <grid margin="1,0,0,6"> <button style="{staticresource textprimarybuttonstyle}" tag="{binding}" click="button_click_1"> <stackpanel orientation="horizontal"> <textblock text="{binding title}" margin="3,-7,10,10" style="{staticresource groupheadertextstyle}" /> <textblock text="{staticresource chevronglyph}" fontfamily="segoe ui symbol" margin="0,-7,0,10" style="{staticresource groupheadertextstyle}"/> </stackpanel> </button> </grid> </datatemplate> </groupstyle.headertemplate> <groupstyle.panel> <itemspaneltemplate> <variablesizedwrapgrid width="800" height="500" orientation="horizontal" itemheight="150" itemwidth="200" margin="0,0,24,0"/> </itemspaneltemplate> </groupstyle.panel> </groupstyle> </gridview.groupstyle> </controls:variablegridview>
as simple as this, i was able to get a basic dashboard up and running. the most complex part about this entire infrastructure is binding, as a lot of what is shown depends on proper binding to the grouped item collection, so pay close attention to that.
Opinions expressed by DZone contributors are their own.
Comments