context: using System;
  using System.Collections;
  using System.ComponentModel;
  using System.Diagnostics;
  
  namespace BrightIdeasSoftware
  {
      /// <summary>
      /// A TreeDataSourceAdapter knows how to build a tree structure from a binding list.
      /// </summary>
      /// <remarks>To build a tree</remarks>
      public class TreeDataSourceAdapter : DataSourceAdapter
      {
          #region Life and death
  
          /// <summary>
          /// Create a data source adaptor that knows how to build a tree structure
          /// </summary>
          /// <param name="tlv"></param>
          public TreeDataSourceAdapter(DataTreeListView tlv)
              : base(tlv) {
              this.treeListView = tlv;
              this.treeListView.CanExpandGetter = delegate(object model) { return this.CalculateHasChildren(model); };
              this.treeListView.ChildrenGetter = delegate(object model) { return this.CalculateChildren(model); };
          }
  
          #endregion
  
          #region Properties
  
          /// <summary>
          /// Gets or sets the name of the property/column that uniquely identifies each row.
          /// </summary>
          /// <remarks>
          /// <para>
          /// The value contained by this column must be unique across all rows 
          /// in the data source. Odd and unpredictable things will happen if two
          /// rows have the same id.
          /// </para>
          /// <para>Null cannot be a valid key value.</para>
          /// </remarks>
          public virtual string KeyAspectName {
              get { return keyAspectName; }
              set {
                  if (keyAspectName == value)
                      return;
                  keyAspectName = value;
                  this.keyMunger = new Munger(this.KeyAspectName);
                  this.InitializeDataSource();
              }
          }
          private string keyAspectName;
  
          /// <summary>
          /// Gets or sets the name of the property/column that contains the key of
          /// the parent of a row.
          /// </summary>
          /// <remarks>
          /// <para>
          /// The test condition for deciding if one row is the parent of another is functionally
          /// equivilent to this:
          /// <code>
          /// Object.Equals(candidateParentRow[this.KeyAspectName], row[this.ParentKeyAspectName])
          /// </code>
          /// </para>
          /// <para>Unlike key value, parent keys can be null but a null parent key can only be used
          /// to identify root objects.</para>
          /// </remarks>
          public virtual string ParentKeyAspectName {
              get { return parentKeyAspectName; }
              set {
                  if (parentKeyAspectName == value)
                      return;
                  parentKeyAspectName = value;
                  this.parentKeyMunger = new Munger(this.ParentKeyAspectName);
                  this.InitializeDataSource();
              }
          }
          private string parentKeyAspectName;
  
          /// <summary>
          /// Gets or sets the value that identifies a row as a root object.
          /// When the ParentKey of a row equals the RootKeyValue, that row will
          /// be treated as root of the TreeListView.
          /// </summary>
          /// <remarks>
          /// <para>
          /// The test condition for deciding a root object is functionally
          /// equivilent to this:
          /// <code>
          /// Object.Equals(candidateRow[this.ParentKeyAspectName], this.RootKeyValue)
          /// </code>
          /// </para>
          /// <para>The RootKeyValue can be null.</para>
          /// </remarks>
          public virtual object RootKeyValue {
              get { return rootKeyValue; }
              set {
                  if (Equals(rootKeyValue, value))
                      return;
                  rootKeyValue = value;
                  this.InitializeDataSource();
              }
          }
          private object rootKeyValue;
  
          /// <summary>
          /// Gets or sets whether or not the key columns (id and parent id) should
          /// be shown to the user.
          /// </summary>
          /// <remarks>This must be set before the DataSource is set. It has no effect
          /// afterwards.</remarks>
          public virtual bool ShowKeyColumns {
              get { return showKeyColumns; }
              set { showKeyColumns = value; }
          }
          private bool showKeyColumns = true;
  
  
          #endregion
  
          #region Implementation properties
  
          /// <summary>
          /// Gets the DataTreeListView that is being managed 
          /// </summary>
          protected DataTreeListView TreeListView {
              get { return treeListView; }
          }
          private readonly DataTreeListView treeListView;
  
          #endregion
  
          #region Implementation
  
          protected override void InitializeDataSource() {
              base.InitializeDataSource();
              this.TreeListView.RebuildAll(true);
          }
  
          protected override void SetListContents() {
              this.TreeListView.Roots = this.CalculateRoots();
          }
  
          protected override bool ShouldCreateColumn(PropertyDescriptor property) {
              // If the property is a key column, and we aren't supposed to show keys, don't show it
              if (!this.ShowKeyColumns && (property.Name == this.KeyAspectName || property.Name == this.ParentKeyAspectName))
                  return false;
  
              return base.ShouldCreateColumn(property);
          }
  
          protected override void HandleListChangedItemChanged(System.ComponentModel.ListChangedEventArgs e) {
              // If the id or the parent id of a row changes, we just rebuild everything.
              // We can't do anything more specific. We don't know what the previous values, so we can't 
              // tell the previous parent to refresh itself. If the id itself has changed, things that used
              // to be children will no longer be children. Just rebuild everything.
              // It seems PropertyDescriptor is only filled in .NET 4 :(
              if (e.PropertyDescriptor != null && 
                  (e.PropertyDescriptor.Name == this.KeyAspectName ||
                   e.PropertyDescriptor.Name == this.ParentKeyAspectName))
                  this.InitializeDataSource();
              else
                  base.HandleListChangedItemChanged(e);
          }
  
          protected override void ChangePosition(int index) {
              // We can't use our base method directly, since the normal position management
              // doesn't know about our tree structure. They treat our dataset as a flat list
              // but we have a collapsable structure. This means that the 5'th row to them
              // may not even be visible to us
  
              // To display the n'th row, we have to make sure that all its ancestors
              // are expanded. Then we will be able to select it.
              object model = this.CurrencyManager.List[index];
              object parent = this.CalculateParent(model);
              while (parent != null && !this.TreeListView.IsExpanded(parent)) {
                  this.TreeListView.Expand(parent);
                  parent = this.CalculateParent(parent);
              }
  
              base.ChangePosition(index);
          }
  
          private IEnumerable CalculateRoots() {
              foreach (object x in this.CurrencyManager.List) {
                  object parentKey = this.GetParentValue(x);
                  if (Object.Equals(this.RootKeyValue, parentKey))
                      yield return x;
              }
          }
  
          private bool CalculateHasChildren(object model) {
              object keyValue = this.GetKeyValue(model);
              if (keyValue == null)
                  return false;
  
              foreach (object x in this.CurrencyManager.List) {
                  object parentKey = this.GetParentValue(x);
                  if (Object.Equals(keyValue, parentKey))
                      return true;
              }
              return false;
          }
  
          private IEnumerable CalculateChildren(object model) {
              object keyValue = this.GetKeyValue(model);
              if (keyValue != null) {
                  foreach (object x in this.CurrencyManager.List) {
  
gt:                     object parentKey = this.GetParentValue(x);