Click here to Skip to main content
15,885,546 members
Please Sign up or sign in to vote.
4.50/5 (2 votes)
See more:
Hi,

I have a collection which internally having sub collections recursively. its some thing like fallowing

XML
class item
{
    public String Field1;
    public String Field2;
    public ObservableCollection<item> subitems=new ObservableCollection<item>();
}

public ObservableCollection<item> items=new ObservableCollection<item>();



this i prepared to bind with a treeGridControl. now there is a need to bind this collection with a list box. if i bind this collection to a list box it is showing only 1st level objects. if i want to bind all the items recursively to the listbox, how can i. please help me.

Thanks
Easwar.
Posted
Updated 4-Jun-12 0:53am
v2

Here is a complete solution whereby a hierarchical data source is flattened and bound to a listbox:


Step 1: Add this extension class.

C#
// -----------------------------------------------------------------------
// <copyright file="Extensions.cs" company="">
// Source: http://darkorbit.net/articles/flatten-a-c-hierarchy.html
// </copyright>
// -----------------------------------------------------------------------
using System.Collections.Generic;
using System;

namespace HierarchicalData.Extensions
{
    public static class IEnumerableExtensions
    {
        /// <summary>
        /// Flattens an object hierarchy.
        /// </summary>
        /// <param name="rootLevel">The root level in the hierarchy.</param>
        /// <param name="nextLevel">A function that returns the next level below a given item.</param>
        /// <returns><![CDATA[An IEnumerable<T> containing every item from every level in the hierarchy.]]></returns>
        public static IEnumerable<T> Flatten<T>(this IEnumerable<T> rootLevel, Func<T, IEnumerable<T>> nextLevel)
        {
            List<T> accumulation = new List<T>();
            accumulation.AddRange(rootLevel);
            flattenLevel<T>(accumulation, rootLevel, nextLevel);
            return accumulation;
        }

        /// <summary>
        /// Recursive helper method that traverses a hierarchy, accumulating items along the way.
        /// </summary>
        /// <param name="accumulation">A collection in which to accumulate items.</param>
        /// <param name="currentLevel">The current level we are traversing.</param>
        /// <param name="nextLevel">A function that returns the next level below a given item.</param>
        private static void flattenLevel<T>(List<T> accumulation, IEnumerable<T> currentLevel, Func<T, IEnumerable<T>> nextLevel)
        {
            foreach (T item in currentLevel)
            {
                accumulation.AddRange(currentLevel);
                flattenLevel<T>(accumulation, nextLevel(item), nextLevel);
            }
        }
    }
}



Step 2: The XAML. (The Listbox could be bound differently if you want)

XML
<Window x:Class="HierarchicalData.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" 
        Height="600" 
        Width="640">
    
    <StackPanel x:Name="LayoutRoot" Background="White">
        <StackPanel.Resources>
            <HierarchicalDataTemplate x:Key="ChildTemplate">
                <TextBlock FontStyle="Italic" Text="{Binding Path=Field1}" />
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate x:Key="NameTemplate" 
            ItemsSource="{Binding Path=subitems}" 
            ItemTemplate="{StaticResource ChildTemplate}">
                <TextBlock Text="{Binding Path=Field1}" FontWeight="Bold" />
            </HierarchicalDataTemplate>
        </StackPanel.Resources>
        <StackPanel Orientation="Vertical">
            <TreeView Width="400"  
                          Height="300" 
                          ItemsSource="{Binding}" 
                          ItemTemplate="{StaticResource NameTemplate}" 
                          x:Name="myTreeView"/>
            <ListBox Width="400"  
                          Height="300" 
                          ItemsSource="{Binding}" 
                          ItemTemplate="{StaticResource NameTemplate}" 
                          x:Name="myListBox"/>
        </StackPanel>
    </StackPanel>
</Window>



Step 3: The class. Note the line which has ".Flatten" - that's the bit that does the magic. (In this case code behind for simplicity)

C#
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using HierarchicalData.Extensions;

namespace HierarchicalData
{
    public partial class MainWindow : Window
    {
        static public ObservableCollection<item> Topics = new ObservableCollection<item>();
        static public List<item> FlatTopics = new List<item>();

        public MainWindow()
        {
            InitializeComponent();

            // Initialise some dummy data
            Topics.Add(new item("Orange", "Fruit"));
            Topics.Add(new item("Banana", "Fruit"));
            item DataGridTopic = new item("Gift Set", "Toiletries");
            DataGridTopic.subitems.Add(new item("Shampoo", "Toiletries"));
            DataGridTopic.subitems.Add(new item("Bath Bomb", "Toiletries"));
            DataGridTopic.subitems.Add(new item("Scented Candle", "Household"));
            Topics.Add(DataGridTopic);

            // Add the data to the controls
            myTreeView.DataContext = Topics;
            myListBox.DataContext = Topics.Flatten(n => n.subitems).Distinct().ToList();
            
        }
    }

    public class item
    {
        public string Field1 { get; set; }
        public string Field2 { get; set; }
        private ObservableCollection<item> subitemsValue = new ObservableCollection<item>();
        public ObservableCollection<item> subitems
        {
            get
            {
                return subitemsValue;
            }
            set
            {
                subitemsValue = value;
            }
        }
        public item() { }
        public item(string field1, string field2)
        {
            Field1 = field1;
            Field2 = field2;
        }
    }
}


Hopefully that's what you're after. Please mark as answer if it helps.

Sources:
 
Share this answer
 
v3
Comments
koteswararao_m 13-Jun-12 1:22am    
Thank you so much AdamDavidHill
This rather depends how many levels you have - is it predictable or variable, e.g. always 2 levels or sometimes 2 and sometimes more?

If you don't have a predictable number of levels then you ideally do want to use something like a tree-based control. If it's say, only two levels, then you could feasibly bind it to a pair of listboxes.

There are two approaches with this - one is to have the listboxes separate, in a master / detail arrangement. Here you would make a selection in the first list and the contents of the second would change to the contents of that one. The other approach is to nest one listbox inside the other.

Hope that helps.
 
Share this answer
 
Comments
koteswararao_m 5-Jun-12 1:43am    
Hi AdamDavidHill,

Thanks for giving me reply on my doubt. In my case the number of levels are unpredictable, it might be more 2. already i am using a tree-view to visualize that collection. as part of another view i need to bind the same collection to a list-box where i need to show all the items in a single level.
Is it possible with list-view ?. if not can u please suggest any other control where i can show all the items of the hierarchical collection as the items of a Control whose items panel can be Wrap-panel.
Adam David Hill 5-Jun-12 6:21am    
Okay can I check I understand you correctly, first? You have some data like this...

A
-A1
--A1i
--A1ii
-A2
--A2i
B
-B1
C

And want it to appear like this in a list box...? That is, every item at every level but flattened into a single list irrespective of their place in the hierarchy?

A
A1
A1i
A1ii
A2
A2i
B
B1
C

(Hope the formatting of this comment will be preserved...)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900