locked
Semantic Zoom - Show letters containing no items

    Question

  • How can I show letters of the alphabet, which contain no items beginning with that letter (in gray) within the ZoomedOutView view of my semantic zoom control?

    I want to achieve something like this (excluding 'Social', 'Favorites' and '#'): 

    but I end up with this:

    MetropolitanDataSource.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace EELL
    {
        using System;
        using Windows.UI.Xaml.Data;
        using Windows.UI.Xaml.Media;
    
        // To significantly reduce the sample data footprint in your production application, you can set
        // the DISABLE_SAMPLE_DATA conditional compilation constant and disable sample data at runtime.
    #if DISABLE_SAMPLE_DATA
        internal class SampleDataSource { }
    #else
    
        public class Item : System.ComponentModel.INotifyPropertyChanged
        {
            public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
                }
            }
    
            private string _Station = string.Empty;
            public string Station
            {
                get
                {
                    return this._Station;
                }
    
                set
                {
                    if (this._Station != value)
                    {
                        this._Station = value;
                        this.OnPropertyChanged("Station");
                    }
                }
            }
    
            private string _Zone = string.Empty;
            public string Zone
            {
                get
                {
                    return this._Zone;
                }
    
                set
                {
                    if (this._Zone != value)
                    {
                        this._Zone = value;
                        this.OnPropertyChanged("Zone");
                    }
                }
            }
    
            private string _Link = string.Empty;
            public string Link
            {
                get
                {
                    return this._Link;
                }
    
                set
                {
                    if (this._Link != value)
                    {
                        this._Link = value;
                        this.OnPropertyChanged("Link");
                    }
                }
            }
        }
    
        public class GroupInfoList<T> : List<object>
        {
    
            public object Key { get; set; }
    
    
            public new IEnumerator<object> GetEnumerator()
            {
                return (System.Collections.Generic.IEnumerator<object>)base.GetEnumerator();
            }
        }
    
    
        public class StoreData
        {
            public StoreData()
            {
                Item item;
    
                item = new Item();
                item.Station = "Aldgate";
                item.Link = "/Lines and Stations/Metropolitan/Aldgate_(Metropolitan).xaml";
                Collection.Add(item);
    
    
                item = new Item();
                item.Station = "Moorgate";
                item.Link = "/Lines and Stations/Metropolitan/MOG_(Metropolitan).xaml";
                Collection.Add(item);
            }
    
    
    
            private ItemCollection _Collection = new ItemCollection();
    
            public ItemCollection Collection
            {
                get
                {
                    return this._Collection;
                }
            }
    
            internal List<GroupInfoList<object>> GetGroupsByCategory()
            {
                List<GroupInfoList<object>> groups = new List<GroupInfoList<object>>();
    
                var query = from item in Collection
                            orderby ((Item)item).Zone
                            group item by ((Item)item).Zone into g
                            select new { GroupName = g.Key, Items = g };
                foreach (var g in query)
                {
                    GroupInfoList<object> info = new GroupInfoList<object>();
                    info.Key = g.GroupName;
                    foreach (var item in g.Items)
                    {
                        info.Add(item);
                    }
                    groups.Add(info);
                }
    
                return groups;
            }
    
            internal List<GroupInfoList<object>> GetGroupsByLetter()
            {
                List<GroupInfoList<object>> groups = new List<GroupInfoList<object>>();
    
                var query = from item in Collection
                            orderby ((Item)item).Station
                            group item by ((Item)item).Station[0] into g
                            select new { GroupName = g.Key, Items = g };
                foreach (var g in query)
                {
                    GroupInfoList<object> info = new GroupInfoList<object>();
                    info.Key = g.GroupName;
                    foreach (var item in g.Items)
                    {
                        info.Add(item);
                    }
                    groups.Add(info);
                }
    
                return groups;
    
            }
        }
    
        // Workaround: data binding works best with an enumeration of objects that does not implement IList
        public class ItemCollection : IEnumerable<Object>
        {
            private System.Collections.ObjectModel.ObservableCollection<Item> itemCollection = new System.Collections.ObjectModel.ObservableCollection<Item>();
    
            public IEnumerator<Object> GetEnumerator()
            {
                return itemCollection.GetEnumerator();
            }
    
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
    
            public void Add(Item item)
            {
                itemCollection.Add(item);
            }
        }
    #endif
    }

    Metropolitan_line.xaml.cs

    using Exits_Expert_London_Lite.Common;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices.WindowsRuntime;
    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 Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237
    
    namespace Exits_Expert_London_Lite.Lines_and_Stations.Metropolitan
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class Metropolitan_line : Page
        {
            public Metropolitan_line()
            {
                this.InitializeComponent();
    
                StoreData _storeData = null;
    
                // creates a new instance of the sample data
                _storeData = new StoreData();
    
                // sets the list of categories to the groups from the sample data
                List<GroupInfoList<object>> dataLetter = _storeData.GetGroupsByLetter();
                // sets the CollectionViewSource in the XAML page resources to the data groups
                cvs2.Source = dataLetter;
                // sets the items source for the zoomed out view to the group data as well
                (semanticZoom.ZoomedOutView as ListViewBase).ItemsSource = cvs2.View.CollectionGroups;
            }
    
            #region Data Visualization
            /// <summary>
            /// We will visualize the data item in asynchronously in multiple phases for improved panning user experience 
            /// of large lists.  In this sample scneario, we will visualize different parts of the data item
            /// in the following order:
            /// 
            ///     1) Placeholders (visualized synchronously - Phase 0)
            ///     2) Tilte (visualized asynchronously - Phase 1)
            ///     3) Image (visualized asynchronously - Phase 2)
            ///
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="args"></param>
            void ItemsGridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
            {
                ItemViewer iv = args.ItemContainer.ContentTemplateRoot as ItemViewer;
    
                if (args.InRecycleQueue == true)
                {
                    iv.ClearData();
                }
                else if (args.Phase == 0)
                {
                    iv.ShowPlaceholder(args.Item as Item);
    
                    // Register for async callback to visualize Title asynchronously
                    args.RegisterUpdateCallback(ContainerContentChangingDelegate);
                }
                else if (args.Phase == 1)
                {
                    iv.ShowStation();
                    args.RegisterUpdateCallback(ContainerContentChangingDelegate);
                }
                else if (args.Phase == 2)
                {
                    iv.ShowZone();
                }
    
    
                // For imporved performance, set Handled to true since app is visualizing the data item
                args.Handled = true;
            }
    
            /// <summary>
            /// Managing delegate creation to ensure we instantiate a single instance for 
            /// optimal performance. 
            /// </summary>
            private TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs> ContainerContentChangingDelegate
            {
                get
                {
                    if (_delegate == null)
                    {
                        _delegate = new TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs>(ItemsGridView_ContainerContentChanging);
                    }
                    return _delegate;
                }
            }
            private TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs> _delegate;
    
            #endregion //Data Visualization
    
        }
    }

    Wednesday, February 11, 2015 12:29 PM

All replies

  • Hi m.findlay93,

    Thanks for your screenshot also code snippet.

    Let me explain you how Semantic zoom control works, we have ZoomInView also ZoomOutView, in the ZoomOutView, we binding the group information, let's say if there is no such group information existing in your data source, how could you display the items on your UI.

    If you need to display the empty group, you need to set them in your data source, for instance your data source should looks like below, to display the color you can calculate whether the group have items.

    No item on C group, but we still put C in our data source.

    - A
    - - item 1
    - - item 2
    - - item 3
    - B
    - - item 4
    - C
    - D
    - - item 5
    - - item 6
    .
    .
    .
    - Z
    --James

    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.


    Thursday, February 12, 2015 7:23 AM
    Moderator
  • I already know how to add items in my data source but based on the code I have provided, what needs to be added or changed in order to achieve this?
    Sunday, February 15, 2015 6:17 PM
  • Change your GetGroupsByLetter function to add empty groups for the empty letters. Right now it adds groups only for the letters that have items, so those are the only groups which show up.
    Sunday, February 15, 2015 8:09 PM
    Moderator
  • I see what you mean but not sure what to change it to. Are there any code samples around depicting this?
    Sunday, February 15, 2015 11:01 PM
  • I'm not aware of any. It should be a pretty straightforward fix. Just loop through the alphabet in GetGroupsByLetter and add groups for each letter.
    • Proposed as answer by Jamles HezModerator Thursday, February 19, 2015 7:37 AM
    • Unproposed as answer by m.findlay93 Monday, February 23, 2015 10:42 PM
    Wednesday, February 18, 2015 3:16 AM
    Moderator
  • To clarify, what do you mean by 'loop through'?
    Friday, February 20, 2015 5:49 PM
  • Write a loop (for loop, while loop, foreach loop, whichever makes you happy) to iterate through all of the letters of the alphabet and add those letters to your GroupInfoList<>.

    string letters[] = { "a","b","c","d","e","f", ... "z" };
    
    foreach(string letter in letters)
    {
       GroupInfoList<object> info = new GroupInfoList<object();
       info.Key = letter;
    
       // Add group info for items beginning with letter, if any
    }

    Saturday, February 21, 2015 12:59 AM
    Moderator
  • What did you mean by
       // Add group info for items beginning with letter, if any
    
    ?
    Tuesday, February 24, 2015 6:29 PM
  • Basically what you're already thing in your existing GetGroupsByLetter function. Do you understand what the code you originally posted does?
    Tuesday, February 24, 2015 7:36 PM
    Moderator
  • Yes, it only shows the letters that contain children items.

    Please have a look at my code snippet below and tell me what needs to change:

    internal List<GroupInfoList<object>> GetGroupsByLetter()
        {
          var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToList();
            var groupByAlphabets = from letter in letters
                       select new
                       {
                           Key = letter,
                           Items = (from item in Collection
                                    where ((Item)item).Station.StartsWith(letter.ToString(), StringComparison.CurrentCultureIgnoreCase)
                                    orderby ((Item)item).Station
                                    group item by ((Item)item).Station[0] into g
                                    select g)
                       };
    
            List<GroupInfoList<object>> groups = new List<GroupInfoList<object>>();
    
            foreach (var g in groupByAlphabets)
            {
                GroupInfoList<object> info = new GroupInfoList<object>();
                info.Key = g.Key;
                foreach (var item in g.Items)
                {
                    info.Add(item);
                }
                groups.Add(info);
            }
    
            return groups;
        }

    Sunday, March 01, 2015 2:39 PM