c# - UWP ObservableCollection sorting and grouping -


in uwp apps, how can group , sort observablecollection , keep live notification goodness?

in simple uwp examples i've seen, there viewmodel exposes observablecollection bound listview in view. when items added or removed observablecollection, listview automatically reflects changes reacting inotifycollectionchanged notifications. works fine in case of unsorted or ungrouped observablecollection, if collection needs sorted or grouped, there seems no readily apparent way preserve update notifications. what's more, changing sort or group order on fly seems throw significant implementation issues.

++

take scenario have existing datacache backend exposes observablecollection of simple class contact.

public class contact {     public string firstname { get; set; }     public string lastname { get; set; }     public string state { get; set; } } 

this observablecollection changes on time, , want present realtime grouped , sorted list in view updates in response changes in datacache. want give user option switch grouping between lastname , state on fly.

++

in wpf world, relatively trivial. can create simple viewmodel referencing datacache presents cache's contacts collection as-is.

public class wpfviewmodel  {     public wpfviewmodel()     {         _cache = getcache();     }      cache _cache;      public observablecollection<contact> contacts     {         { return _cache.contacts; }     } } 

then can bind view implement collectionviewsource , sort , group definitions xaml resources.

<window .....    xmlns:scm="clr-namespace:system.componentmodel;assembly=windowsbase">     <window.datacontext>       <local:wpfviewmodel />    </window.datacontext>      <window.resources>         <collectionviewsource x:key="cvs" source="{binding contacts}" />         <propertygroupdescription x:key="stategroup" propertyname="state" />         <propertygroupdescription x:key="initialgroup" propertyname="lastname[0]" />         <scm:sortdescription x:key="statesort" propertyname="state" direction="ascending" />         <scm:sortdescription x:key="lastsort" propertyname="lastname" direction="ascending" />         <scm:sortdescription x:key="firstsort" propertyname="firstname" direction="ascending" />     </window.resources>      <grid>         <grid.rowdefinitions>             <rowdefinition height="*" />             <rowdefinition height="auto" />         </grid.rowdefinitions>          <listview itemssource="{binding source={staticresource cvs}}">             <listview.itemtemplate>                 <datatemplate>                     <grid>                         <grid.columndefinitions>                             <columndefinition width="100" />                             <columndefinition width="100" />                             <columndefinition width="*" />                         </grid.columndefinitions>                         <textblock text="{binding lastname}" />                         <textblock text="{binding firstname}" grid.column="1" />                         <textblock text="{binding state}" grid.column="2" />                     </grid>                 </datatemplate>             </listview.itemtemplate>             <listview.groupstyle>                 <groupstyle>                     <groupstyle.headertemplate>                         <datatemplate>                             <grid background="gainsboro">                                 <textblock fontweight="bold"                                             fontsize="14"                                             margin="10,2"                                            text="{binding name}"/>                             </grid>                         </datatemplate>                     </groupstyle.headertemplate>                 </groupstyle>             </listview.groupstyle>         </listview>          <stackpanel orientation="horizontal" grid.row="1">             <button content="group initial" click="initialgroupclick" />             <button content="group state" click="stategroupclick" />         </stackpanel>      </grid> </window> 

then when user clicks on groupby buttons @ bottom of window can can group , sort on fly in code-behind.

private void initialgroupclick(object sender, routedeventargs e) {      var cvs = findresource("cvs") collectionviewsource;      var initialgroup = (propertygroupdescription)findresource("initialgroup");      var firstsort = (sortdescription)findresource("firstsort");      var lastsort = (sortdescription)findresource("lastsort");       using (cvs.deferrefresh())      {          cvs.groupdescriptions.clear();          cvs.sortdescriptions.clear();          cvs.groupdescriptions.add(initialgroup);          cvs.sortdescriptions.add(lastsort);          cvs.sortdescriptions.add(firstsort);      } }  private void stategroupclick(object sender, routedeventargs e) {      var cvs = findresource("cvs") collectionviewsource;      var stategroup = (propertygroupdescription)findresource("stategroup");      var statesort = (sortdescription)findresource("statesort");      var lastsort = (sortdescription)findresource("lastsort");      var firstsort = (sortdescription)findresource("firstsort");       using (cvs.deferrefresh())      {          cvs.groupdescriptions.clear();          cvs.sortdescriptions.clear();          cvs.groupdescriptions.add(stategroup);          cvs.sortdescriptions.add(statesort);          cvs.sortdescriptions.add(lastsort);          cvs.sortdescriptions.add(firstsort);      } } 

this works fine, , items updated automatically data cache collection changes. listview grouping , selection remains unaffected collection changes, , new contact items correctly grouped.the grouping can swapped between state , lastname initial user @ runtime.

++

in uwp world, collectionviewsource no longer has groupdescriptions , sortdescriptions collections, , sorting/grouping need carried out @ viewmodel level. closest approach workable solution i've found along lines of microsoft's sample package at

https://github.com/microsoft/windows-universal-samples/tree/master/samples/xamllistview

and article

http://motzcod.es/post/94643411707/enhancing-xamarinforms-listview-with-grouping

where viewmodel groups observablecollection using linq , presents view observablecollection of grouped items

public observablecollection<groupinfolist> groupedcontacts {     observablecollection<groupinfolist> groups = new observablecollection<groupinfolist>();      var query = item in _cache.contacts                 group item item.lastname[0] g                 orderby g.key                 select new { groupname = g.key, items = g };      foreach (var g in query)     {          groupinfolist info = new groupinfolist();          info.key = g.groupname;          foreach (var item in g.items)          {              info.add(item);          }          groups.add(info);     }      return groups; } 

where groupinfolist defined

public class groupinfolist : list<object> {    public object key { get; set; } } 

this @ least grouped collection displayed in view, updates datacache collection no longer reflected in real time. capture datacache's collectionchanged event , use in viewmodel refresh groupedcontacts collection, creates new collection every change in datacache, causing listview flicker , reset selection etc suboptimal.

also swapping grouping on fly seem require seperate observablecollection of grouped items each grouping scenario, , listview's itemsource binding swapped @ runtime.

the rest of i've seen of uwp environment seems extremely useful, i'm surprised find vital grouping , sorting lists throwing obstacles...

anyone know how properly?

i've started putting library called groupedobservablecollection along these lines 1 of apps.

one of key problems needed solve refreshing of original list used create group, i.e. didn't want user searching different criteria cause whole list refreshed, differences.

in current form won't answer sorting questions right now, might starting point others.


Comments

Popular posts from this blog

SVG stroke-linecap doesn't work for circles in Firefox? -

routes - Laravel 4 Wildcard Routing to Different Controllers -

cross browser - XSLT namespace-alias Not Working in Firefox or Chrome -