vbAccelerator - Contents of code file: MultiColumnList_MultiColumnList.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace vbAccelerator.Components.ListBox
{
   #region SListBoxFindDirection
   public enum SListBoxFindDirection : int
   {
      up = -1,
      down = 1
   }
   #endregion

   #region SListBox
   /// <summary>
   /// SListBox is a flexible extended ListBox allowing for multiple columns,
   /// hierarchical groups with expand/collapse, invisible rows,
   /// rows which span multiple columns and icons in cells.
   /// </summary>
   public class SListBox : System.Windows.Forms.ListBox
   {
      #region ObjectCollection
      /// <summary>
      /// Strongly-typed collection of objects within the SListBox
      /// </summary>
      new public class ObjectCollection :
       System.Windows.Forms.ListBox.ObjectCollection 
      {
         #region Member variables
         private SListBox owner;
         #endregion

         new public SListItem this[int index]
         {
            get
            {
               return (SListItem)base[index];
            }
            set
            {
               base[index] = value;
            }
         }
         /// <summary>
         /// Adds an item to the list of items in the SListBox
         /// </summary>
         /// <param name="item">Item to add</param>
         /// <returns>Zero-based index of the item in the collection</returns>
         public System.Int32 Add ( SListItem item )
         {
            item.Owner = this.owner;
            return base.Add(item);
         }
         /// <summary>
         /// Adds an array of items to the list of items in the SListBox
         /// </summary>
         /// <param name="items">An array of objects to add to the list</param>
         public void AddRange ( SListItem[] items )
         {
            foreach (SListItem item in items)
            {
               item.Owner = this.owner;
            }
            base.AddRange(items);
         }
         /// <summary>
         /// Adds the items of an existing SListBox.ObjectCollection to the list
         /// of items in this SListBox
         /// </summary>
         /// <param name="value">An SlistBox.ObjectCollection to load into this
          collection</param>
         public void AddRange ( ObjectCollection value )
         {
            base.AddRange(value);
         }
         /// <summary>
         /// Determines whether the specified item is located within the
          collection
         /// </summary>
         /// <param name="value">An SListItem containing the item to locate in
          the collection</param>
         /// <returns>True if the item is located within the collection;
          otherwise false.</returns>
         public bool Contains(SListItem value)
         {
            return base.Contains(value);
         }
         /// <summary>
         /// Copies the entire collection into an existing array of objects at
          a specified location within the array
         /// </summary>
         /// <param name="dest">The object array in which the items from the
          collection are copied to</param>
         /// <param name="arrayIndex">The location within the destination array
          to copy the items from the collection to</param>
         public void CopyTo(SListItem[] dest , System.Int32 arrayIndex )
         {
            base.CopyTo(dest, arrayIndex);
         }
         /// <summary>
         /// Returns the index within the collection of the specified item.
         /// </summary>
         /// <param name="value">The SListItem to locate in the
          collection.</param>
         /// <returns>The zero-based index where the item is located within the
          collection, otherwise -1</returns>
         public int IndexOf(SListItem value)
         {
            return base.IndexOf(value);
         }
         /// <summary>
         /// Inserts an item into the list box at the specified index.
         /// </summary>
         /// <param name="index">The zero-based index location where the item
          is inserted.</param>
         /// <param name="item">The SListItem to insert.</param>
         public void Insert(int index, SListItem item)
         {
            item.Owner = this.owner;
            base.Insert(index, item);
         }

         /// <summary>
         /// Initializes a new instance of the ObjectCollection with an array
          of SListItem objects
         /// </summary>
         /// <param name="owner">SListBox that owns the collection</param>
         /// <param name="value">An array of SListItem objects to add to the
          collection</param>
         public ObjectCollection ( SListBox owner , SListItem[] value ) :
          base(owner, value)
         {
            foreach (SListItem item in value)
            {
               item.Owner = owner;
            }
            this.owner = owner;
         }
         /// <summary>
         /// Initializes a new instance of the ObjectCollection based on
          another ObjectCollection.
         /// </summary>
         /// <param name="owner">SListBox that owns the collection</param>
         /// <param name="value">An ObjectCollection from which the contents
          are copied to this collection</param>
         public ObjectCollection ( SListBox owner , ObjectCollection value ) :
          base(owner, value)
         {
            this.owner = owner;
         }
         /// <summary>
         /// Initializes a new instance of the ObjectCollection.
         /// </summary>
         /// <param name="owner">The SListBox that owns the collection</param>
         public ObjectCollection ( SListBox owner ) : base(owner)
         {
            this.owner = owner;
         }
      }
      #endregion

      #region Member Variables
      private int lastSelectedIndex = -1;
      private SListColumnCollection columns = null;
      private ImageList imgList = null;
      private int lastWidth = 0;
      private bool showGroupingRows = false;
      private int[] sortColumnArray = new int[0];
      private SortOrder[] sortColumnOrder = new SortOrder[0];
      #endregion
      
      /// <summary>
      /// Gets/sets whether grouping rows for each grouped
      /// column are automatically added to the control 
      /// when the GroupOrder property of a column is
      /// changed.
      /// </summary>
      public bool ShowGroupingRows
      {
         get
         {
            return this.showGroupingRows;
         }
         set
         {
            this.showGroupingRows = value;
            if (value)
            {
               addGroupedRows();
            }
            else
            {
               removeGroupedRows();
            }
         }
      }

      /// <summary>
      /// Gets/sets the ImageList used as source of icons
      /// </summary>
      public System.Windows.Forms.ImageList ImageList
      {
         get
         {
            return this.imgList;
         }
         set
         {
            this.imgList = value;
         }
      }

      /// <summary>
      /// Returns the collection of columns associated 
      /// with the control
      /// </summary>
      public SListColumnCollection Columns
      {
         get
         {
            return this.columns;
         }
      }

      /// <summary>
      /// Determines whether the specified item is expanded or not
      /// </summary>
      /// <param name="item">Item to check</param>
      /// <returns>True if expanded, False otherwise</returns>
      public virtual bool ItemIsExpanded(
         SListItem item)
      {
         bool isExpanded = true;
         int index = this.Items.IndexOf(item);
         if (index > -1)
         {
            if (index < this.Items.Count)
            {
               SListItem testItem = this.Items[index + 1];
               if (!testItem.Visible)
               {
                  isExpanded = false;
               }
            }
         }
         return isExpanded;
      }

      /// <summary>
      /// Expands or Collapses any child items under the specified
      /// item.
      /// </summary>
      /// <param name="item">Item to expand/collapse for</param>
      /// <param name="expand">True if expanding, False otherwise</param>
      public virtual void ExpandCollapse(
         SListItem item,
         bool expand
         )
      {
         int index = this.Items.IndexOf(item);
         if (index > -1)
         {
            for (int i = index + 1; i < this.Items.Count; i++)
            {
               SListItem testItem = this.Items[i];
               if (testItem.Indentation > item.Indentation)
               {
                  if (testItem.Visible != expand)
                  {
                     testItem.Visible = expand;

                  }
               }
               else
               {
                  break;
               }
            }
         }
      }

      internal int[] SortColumnArray()
      {
         return this.sortColumnArray;
      }
      internal SortOrder[] SortColumnOrder()
      {
         return this.sortColumnOrder;
      }


      /// <summary>
      /// Called when the SortOrder property of a column is changed
      /// </summary>
      /// <param name="columnIndex">Column index to sort by</param>
      public virtual void SortByColumn(
         int columnIndex
         )
      {

         Array sortArray;
         Array indexArray = Array.CreateInstance(typeof(SListItem),
          this.Items.Count);
         int i = 0;
         int index = 0;

         if (columnIndex == 0)
         {
            sortArray = Array.CreateInstance(typeof(SListItem),
             this.Items.Count);
            foreach (SListItem item in this.Items)
            {
               sortArray.SetValue(item, i);
               indexArray.SetValue(item, i);
               i++;
            }
            Array.Sort(sortArray, indexArray);
            foreach (SListItem item in indexArray)
            {
               this.Items[index] = item;
               index++;
            }            
         }
         else
         {
            // Create an array of the column items:
            sortArray = Array.CreateInstance(typeof(SListColumnItem),
             this.Items.Count);
            foreach (SListItem item in this.Items)
            {
               sortArray.SetValue(item[columnIndex], i);
               indexArray.SetValue(item, i);
               i++;
            }
            Array.Sort(sortArray, indexArray);
            if (this.columns[columnIndex].SortOrder == SortOrder.Descending)
            {
               Array.Reverse(sortArray);
               Array.Reverse(indexArray);
            }
            // now modify the position of all the items in the box according 
            // to the sort order:
            foreach (SListItem item in indexArray)
            {
               this.Items[index] = item;
               index++;
            }
         }
      }

      /// <summary>
      /// Gets the collection of SListItems in this ListBox
      /// </summary>
      new public ObjectCollection Items
      {
         get
         {
            return (ObjectCollection)base.Items;
         }
      }

      /// <summary>
      /// Finds the next visible item index in the control, if any.
      /// </summary>
      /// <param name="index">Zero-based index of the item to start at</param>
      /// <param name="direction">Direction to search in</param>
      /// <returns>The index of the next visible item if found, -1
       otherwise</returns>
      public int NextVisibleItemIndex(
         int index,
         SListBoxFindDirection direction
         )
      {
         int nextIndex = -1;
         SListItem item;

         if (direction == SListBoxFindDirection.down)
         {
            int i = index;
            bool found = false;
            while (!found)
            {
               i++;
               if (i == this.Items.Count)
               {
                  // no next item to select.
                  // Select the current item if possible:
                  if (lastSelectedIndex != -1)
                  {
                     nextIndex = lastSelectedIndex;
                     found = true;
                  }
                  else
                  {
                     i = this.SelectedIndex;
                     while (!found)
                     {
                        i--;
                        if (i < 0)
                        {
                           // nothing to select
                           found = true;
                        }
                        else
                        {
                           item = this.Items[i];
                           if ((item.Height > 0) && (item.Visible))
                           {
                              nextIndex  = i;
                              found = true;
                           }
                        }
                     }
                  }
               }
               else
               {
                  item = this.Items[i];
                  if ((item.Height > 0) && (item.Visible))
                  {
                     nextIndex  = i;
                     found = true;
                  }
               }
            }
         }
         else
         {
            // trying to select the prior item   
            int i = index;
            bool found = false;
            while (!found)
            {
               i--;
               if (i < 0)
               {
                  // no prior item to select.
                  // select the current item if possible
                  if (lastSelectedIndex != -1)
                  {
                     nextIndex = lastSelectedIndex;
                     found = true;
                  }
                  else
                  {
                     i = this.SelectedIndex;
                     while (!found)
                     {
                        i++;
                        if (i == this.Items.Count)
                        {
                           // nothing to select
                           found = true;
                        }
                        else
                        {
                           item = this.Items[i];
                           if ((item.Height > 0) && (item.Visible))
                           {
                              nextIndex  = i;
                              found = true;
                           }
                        }
                     }
                  }
               }
               else
               {
                  item = this.Items[i];
                  if ((item.Height > 0) && (item.Visible))
                  {
                     nextIndex  = i;
                     found = true;
                  }
               }
            }
         }
         return nextIndex;
      }

      private void removeGroupedRows()
      {
         int i = 0;
         while (i < this.Items.Count)
         {
            if (this.Items[i].GroupRow)
            {
               // it is a grouping row:
               this.Items.RemoveAt(i);
            }
            else
            {
               i++;
            }
         }
      }

      private void addGroupedRows()
      {
         if (this.showGroupingRows)
         {
            if ((this.Items.Count > 0) && (this.sortColumnArray.Length > 0))
            {
               // add the first item:
               SListItem s = null;
               int i = 0;
               int indent = 0;
               foreach (int col in this.sortColumnArray)
               {
                  s = new SListItem(
                     this.Items[i][col].Text
                     );
                  s.ShowColumns = false;
                  s.BackColor = Color.FromKnownColor(KnownColor.Control);
                  s.Indentation = indent;
                  s.GroupRow = true;
                  this.Items.Insert(i, s);
                  i++;
                  indent += 16;
               }
               int itemIndent = indent + 16;
               int lastRow = i;

               bool foundFirst = false;
               while (i < this.Items.Count)
               {
                  //this.Items[i].Indentation += itemIndent;
                  // check if this item matches the last one:
                  indent = 0;
                  foundFirst = false;
                  foreach (int col in this.sortColumnArray)
                  {
                     if (
                      (!this.Items[lastRow][col].Text.Equals(this.Items[i][col].
                     Text)) ||
                        foundFirst )
                     {
                        s = new SListItem(
                           this.Items[i][col].Text
                           );
                        s.ShowColumns = false;
                        s.BackColor = Color.FromKnownColor(KnownColor.Control);
                        s.Indentation = indent;
                        s.GroupRow = true;
                        this.Items.Insert(i, s);
                        i++;
                        lastRow = i;
                        foundFirst = true;
                     }
                     indent += 16;
                  }                  
                  i++;
               }
            }
         }
      }

      internal void SetColumnGroupOrder(
         SListColumn item,
         int order
         )
      {
         if (item.GroupOrder == order)
         {
            // nothing to do
         }
         else
         {
            this.Visible = false;
            removeGroupedRows();
      
            // Check if this item is already part of a group order:
            if (item.GroupOrder > 0)
            {
               // it is already there.
               int existingGroupOrder = item.GroupOrder;
               foreach (SListColumn col in this.columns)
               {
                  if (col.GroupOrder > 0)
                  {
                     if (existingGroupOrder < order)
                     {
                        if ((col.GroupOrder > existingGroupOrder) &&
                         (col.GroupOrder <= order))
                        {
                           col.SetColumnGroupOrder(col.GroupOrder - 1);
                        }
                     }
                     else
                     {
                        if ((col.GroupOrder >= order) && (col.GroupOrder <
                         existingGroupOrder))
                        {
                           col.SetColumnGroupOrder(col.GroupOrder + 1);
                        }
                     }
                  }
               }
               item.SetColumnGroupOrder(order);

            }
            else
            {
               // this is a new item.

               // Determine the group order of this item:
               int maxGroupOrder = 0;
               foreach (SListColumn col in this.columns)
               {
                  if (col.GroupOrder >= order)
                  {
                     col.SetColumnGroupOrder(col.GroupOrder + 1);
                  }
                  if (col.GroupOrder > maxGroupOrder)
                  {
                     maxGroupOrder = col.GroupOrder;
                  }
               }
               if (order > maxGroupOrder)
               {
                  // we add to the end:
                  order = maxGroupOrder + 1;
               }
               item.SetColumnGroupOrder(order);
            }

            ArrayList sca = new ArrayList();
            ArrayList sco = new ArrayList();
            int index = 0;
            foreach (SListColumn col in this.columns)
            {
               if (col.GroupOrder > 0)
               {
                  sca.Add(index);
                  sco.Add(col.SortOrder);
               }
               index++;
            }
            sortColumnArray = (int [])sca.ToArray(index.GetType());
            sortColumnOrder = (SortOrder
             [])sco.ToArray(SortOrder.Ascending.GetType());

            this.SortByColumn(0);
            addGroupedRows();
            this.Visible = true;

         }
      }

      protected override void AddItemsCore ( object[] value )
      {
         base.AddItemsCore(value);
      }
      protected override void SetItemCore ( System.Int32 index , System.Object
       value )
      {
         base.SetItemCore(index, value);
      }
      protected override void SetItemsCore ( System.Collections.IList value )
      {
         base.SetItemsCore(value);
      }
      /// <summary>
      /// Responds to a non-height property change event of an item.
      /// Forces the ListBox to redraw the item.
      /// </summary>
      /// <param name="item"></param>
      public virtual void OnItemChanged(
         SListItem item
         )
      {
         Rectangle rc = this.GetItemRectangle(this.Items.IndexOf(item));
         this.Invalidate(rc);
      }
      /// <summary>
      /// Responds to a height changed event of an item.
      /// In a ListBox, the only way to change the height of an item
      /// once it has been Measured is to remove it and then add it
      /// again.
      /// </summary>
      /// <param name="item">Item whose height has changed</param>
      public virtual void OnHeightChanged(
         SListItem item
         )
      {
         int index = this.Items.IndexOf(item);
         this.Items.RemoveAt(index);
         this.Items.Insert(index, item);
      }
      public virtual void OnColumnChanged(
         SListColumn column
         )
      {
         OnResize(null);
      }      
      /// <summary>
      /// Creates a new instance of the SListBox.ObjectCollection.
      /// </summary>
      /// <returns>An SListBox.ObjectCollection that represents the new item
       collection</returns>
      protected override System.Windows.Forms.ListBox.ObjectCollection
       CreateItemCollection()
      {
         return new ObjectCollection(this);
      }

      /// <summary>
      /// Raises the OnResize event and makes the Horizontal scroll bar visible
      /// if necessary.
      /// </summary>
      /// <param name="e">Event arguments</param>
      protected override void OnResize( System.EventArgs e )
      {
         int width = 0;
         foreach (SListColumn col in this.columns)
         {
            width += col.Width;
         }
         if (width > this.Width)
         {
            this.HorizontalScrollbar = true;
            this.HorizontalExtent = width;
         }
         else
         {
            this.HorizontalScrollbar = false;
         }
         if (width != this.lastWidth)
         {
            // necessary to redraw all the items
            this.Invalidate();
         }
         base.OnResize(e);
      }

      /// <summary>
      /// Calls the ListBox base OnSelectedIndexChanged unless the
      /// item being selected has a height less than 0, in which
      /// case the appropriate next visible item is selected.
      /// </summary>
      /// <param name="e">Event arguments</param>
      protected override void OnSelectedIndexChanged ( System.EventArgs e )
      {
         if ((this.SelectedIndex >= 0) && (this.SelectedIndex <
          this.Items.Count))
         {
            SListItem item = this.Items[this.SelectedIndex];
            if ((item.Height <= 0) || (!item.Visible))
            {
               SListBoxFindDirection direction = SListBoxFindDirection.up;
               if (lastSelectedIndex < this.SelectedIndex)
               {
                  // trying to select the next item
                  direction = SListBoxFindDirection.down;
               }
               this.lastSelectedIndex = -1;
               int index = NextVisibleItemIndex(this.SelectedIndex, direction);
               if (index > -1)
               {
                  this.SelectedIndex = index;
               }
            }
            else
            {
               base.OnSelectedIndexChanged(e);
               this.lastSelectedIndex = this.SelectedIndex;
            }
         }
         else
         {
            base.OnSelectedIndexChanged(e);
         }
      }

      /// <summary>
      /// Draws the SListBox item and raises the DrawItem 
      /// event.
      /// </summary>
      /// <param name="e">DrawItemEventArgs for the item</param>
      protected override void OnDrawItem(DrawItemEventArgs e)
      {
         if ((e.Index >= 0) && (e.Index < this.Items.Count))
         {
            SListItem item = this.Items[e.Index];
            if ((item.Height > 0) && (item.Visible))
            {
               bool selected = ((e.State & DrawItemState.Selected) ==
                DrawItemState.Selected);
               bool rightToLeft = (this.RightToLeft == RightToLeft.Yes);
               if (selected)
               {
                  if (item.Indentation > 0)
                  {
                     if (rightToLeft)
                     {
                        Rectangle indented = new Rectangle(
                           e.Bounds.X, 
                           e.Bounds.Y,
                           e.Bounds.Width - item.Indentation,
                           e.Bounds.Height);
                        //e.Graphics.FillRectangle(SystemBrushes.Highlight,
                         indented);
                     }
                     else
                     {
                        Rectangle indented = new Rectangle(
                           e.Bounds.X + item.Indentation, 
                           e.Bounds.Y,
                           e.Bounds.Width - item.Indentation,
                           e.Bounds.Height);
                        //e.Graphics.FillRectangle(SystemBrushes.Highlight,
                         indented);
                     }
                  }
                  else
                  {
                     //e.Graphics.FillRectangle(SystemBrushes.Highlight,
                      e.Bounds);
                  }
               }
               else
               {
                  //e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
               }
               Rectangle itemRect = e.Bounds;
               itemRect.Inflate(-1, -1);
               item.DrawItem(e.Graphics, imgList, itemRect, this.Font,
                selected, true, rightToLeft);
            }
         }
         base.OnDrawItem(e);
      }

      /// <summary>
      /// Measures the item and raises the MeasureItem event
      /// </summary>
      /// <param name="e">MeasureItemEventArgs for the item</param>
      protected override void OnMeasureItem(MeasureItemEventArgs e)
      {
         if ((e.Index >= 0) && (e.Index < this.Items.Count))
         {
            SListItem item = this.Items[e.Index];
            if (item.Visible)
            {
               e.ItemHeight = item.RenderHeight;
            }
            else
            {
               e.ItemHeight = 0;
            }
            e.ItemWidth = 1024;
         }
         base.OnMeasureItem(e);
      }

      /// <summary>
      /// Initializes a new instance of the SListBox object.
      /// </summary>
      public SListBox()
      {
         this.DrawMode = DrawMode.OwnerDrawVariable;
         this.columns = new SListColumnCollection(this);
      }
   }
   #endregion

   #region SListItemBase
   /// <summary>
   /// Class which SListItems derive from.  Provides base
   /// services for item properties such as text to display,
   /// Font, Colours, indentation etc
   /// </summary>
   public abstract class SListItemBase : IDisposable, IComparable
   {
      private object text = null;
      private string textFormat = "";
      private int iconIndex = -1;
      private StringFormat stringFormat = null;
      private Font font = null;
      private Color foreColor = Color.FromKnownColor(KnownColor.WindowText);
      private Color backColor = Color.FromKnownColor(KnownColor.Window);
      private int indentation = 0;
      private int height = 20;
      private object tag = null;

      /// <summary>
      /// Compares the current instance with another object of the same type.
      /// The class attempts to use the CompareTo method of the text object
      /// for comparision.  If this is not available, the ToString() method 
      /// of this class is used for comparison.
      /// </summary>
      /// <param name="obj">An object to compare with this instance.</param>
      /// <returns>A 32-bit signed integer that indicates the relative order of
       the comparands.  
      /// The return value has these meanings:
      /// Less than zero This instance is less than obj.  
      /// Zero This instance is equal to obj.  
      /// Greater than zero This instance is greater than obj.</returns>
      public virtual int CompareTo ( object obj )
      {
         int res = 0;
         try
         {
            bool doDefault = false;
            IComparable icThis = null;
            try
            {
               icThis = (IComparable)this.text;
            }
            catch
            {
               doDefault = true;
            }
            if (doDefault)
            {
               res = this.ToString().CompareTo(obj.ToString());
            }
            else
            {
               res = icThis.CompareTo(((SListItemBase)obj).Text);
            }
         }
         catch (Exception ex)
         {
            Console.WriteLine("Exception {0}", ex);
         }
         return res;
      }
         
      /// <summary>
      /// Gets/sets the text to display for this item.
      /// Defaults to no text.  The ToString method of the
      /// object will be used to render it unless a TextFormat
      /// string is set, in which case the String.Format method
      /// will be invoked.
      /// </summary>
      public object Text
      {
         get
         {
            return this.text;
         }
         set
         {
            this.text = value;
            OnItemChanged();
         }
      }

      public string TextFormat
      {
         get
         {
            return this.textFormat;
         }
         set
         {
            this.textFormat = value;
            OnItemChanged();
         }
      }

      /// <summary>
      /// Gets/sets the index of the icon within the ImageList
      /// to display for this item.  Set to -1 for no icon.
      /// Defaults to -1.
      /// </summary>
      public int Icon
      {
         get
         {
            return this.iconIndex;
         }
         set
         {
            this.iconIndex = value;
            OnItemChanged();
         }
      }

      /// <summary>
      /// Gets/sets the indentation of this item.
      /// </summary>
      public int Indentation
      {
         get
         {
            return this.indentation;
         }
         set
         {
            this.indentation = value;
            OnItemChanged();
         }
      }
      
      /// <summary>
      /// Gets/sets a specific string format to use
      /// when drawing this item.  Set to null to 
      /// use default text rendering.  Defaults to 0
      /// </summary>      
      public System.Drawing.StringFormat StringFormat
      {
         get
         {
            return this.stringFormat;
         }
         set
         {
            this.stringFormat = value;
            OnItemChanged();
         }
      }
      /// <summary>
      /// Gets/sets a specific font to use
      /// when drawing this item.  When null, the 
      /// owner ListBox font is used for drawing.
      /// Default is null.
      /// </summary>
      public System.Drawing.Font Font
      {
         get
         {
            return this.font;
         }
         set
         {
            this.font = value;
            OnItemChanged();
         }
      }      
      /// <summary>
      /// Gets/sets a customised foreground
      /// color for this item.  Default is
      /// WindowText.
      /// </summary>
      public Color ForeColor
      {
         get
         {
            return this.foreColor;
         }
         set
         {
            this.foreColor = value;
            OnItemChanged();
         }
      }
      /// <summary>
      /// Gets/sets a customised background
      /// color for this item.  Default is
      /// WindowBackground.
      /// </summary>
      public Color BackColor
      {
         get
         {
            return this.backColor;
         }
         set
         {
            this.backColor = value;
            OnItemChanged();
         }
      }
      /// <summary>
      /// Gets/sets an object associated with the item.
      /// </summary>
      public object Tag
      {
         get
         {
            return this.tag;
         }
         set
         {
            this.tag = value;
            OnItemChanged();
         }
      }
      /// <summary>
      /// Gets/sets the height of this item
      /// </summary>
      public int Height
      {
         get
         {
            return this.height;
         }
         set
         {
            this.height = value;
            OnHeightChanged();
         }
      }
      /// <summary>
      /// Called whenever a sizing property is changed
      /// </summary>
      protected virtual void OnHeightChanged()
      {
      }
      /// <summary>
      /// Called whenever a non-sizing property is changed
      /// </summary>
      protected virtual void OnItemChanged()
      {
      }
      /// <summary>
      /// Draws this item.
      /// Used internally to draw the ListBox.
      /// </summary>
      /// <param name="graphics">Graphics object to draw to</param>
      /// <param name="imageList">Source of images</param>
      /// <param name="bounds">Bounding rectangle for the item</param>
      /// <param name="defaultFont">Font to use if the item does not have a
       font specified</param>
      /// <param name="selected">Whether the item should be rendered selected
       or not</param>
      /// <param name="firstItem">Whether the item is the first in the list
       box</param>
      /// <param name="rightToLeft">Whether item should be rendered
       right-to-left</param>
      public virtual void DrawItem(
         Graphics graphics,
         ImageList imageList,
         Rectangle bounds,
         Font defaultFont,
         bool selected,
         bool firstItem,
         bool rightToLeft
         )
      {
         // fill the background:
         Rectangle fillRect = bounds;
         if (firstItem)
         {
            if (rightToLeft)
            {
               fillRect.X = bounds.X;
               fillRect.Y = bounds.Y;
               fillRect.Width = bounds.Width - this.indentation;
               fillRect.Height = bounds.Height;
            }
            else
            {
               fillRect.X = bounds.X + this.indentation;
               fillRect.Y = bounds.Y;
               fillRect.Width = bounds.Width - this.indentation;
               fillRect.Height = bounds.Height;
            }
         }
         if (selected)
         {
            graphics.FillRectangle(SystemBrushes.Highlight, fillRect);
         }
         else
         {
            Brush backBrush = new SolidBrush(this.backColor);
            graphics.FillRectangle(backBrush, fillRect);
            backBrush.Dispose();
         }
   
         // Get the item rectangle:         
         Rectangle itemRect;
         int iconX;
         if (rightToLeft)
         {
            itemRect = new Rectangle(
               bounds.X,
               bounds.Y,
               bounds.Width - this.indentation,
               bounds.Height);
            iconX = bounds.X + bounds.Width - this.indentation;
         }
         else
         {
            itemRect = new Rectangle(
               bounds.X + this.indentation,
               bounds.Y,
               bounds.Width - this.indentation,
               bounds.Height);
            iconX = bounds.X + this.indentation;
         }

         // Draw the icon
         if (imageList != null)
         {
            if (iconIndex > -1)
            {
               if (rightToLeft)
               {
                  iconX -= imageList.ImageSize.Width;
               }
               int iconY = bounds.Top + (bounds.Height -
                imageList.ImageSize.Height) / 2;
               if (iconY < bounds.Top)
               {
                  imageList.Draw(
                     graphics, 
                     iconX,
                     iconY,
                     imageList.ImageSize.Width,
                     bounds.Height, 
                     this.iconIndex);
               }
               else
               {
                  imageList.Draw(
                     graphics, 
                     iconX,
                     iconY,
                     this.iconIndex);
               }
               if (!rightToLeft)
               {
                  iconX += imageList.ImageSize.Width;
               }
            }
         }
      
         Font itemFont = defaultFont;
         if (this.font != null)
         {
            itemFont = this.font;
         }
         Brush itemBrush;
         if (selected)
         {
            itemBrush = new
             SolidBrush(Color.FromKnownColor(KnownColor.HighlightText));
         }
         else
         {
            itemBrush= new SolidBrush(this.foreColor);
         }
         StringFormat format;
         if (this.stringFormat != null)
         {
            format = this.stringFormat;
         }
         else
         {
            format = new StringFormat();
            format.Alignment = StringAlignment.Near;
            format.LineAlignment = StringAlignment.Center;
            format.FormatFlags = StringFormatFlags.LineLimit;
            format.Trimming = StringTrimming.EllipsisCharacter;
         }
         RectangleF textRect;
         if (rightToLeft)
         {
            textRect = new RectangleF(
               (float)bounds.X, (float)bounds.Y, (float)(iconX - bounds.X),
                (float)bounds.Height);
         }
         else
         {
            textRect = new RectangleF(
               (float)iconX, (float)bounds.Y, (float)itemRect.Width,
                (float)bounds.Height);
         }

         // Draw the text:
         graphics.DrawString(
            this.ToString(),
            itemFont,
            itemBrush,
            textRect,
            format);

         itemBrush.Dispose();
         if (this.stringFormat == null)
         {
            format.Dispose();
         }

      }
      /// <summary>
      /// Returns the text of the item.
      /// </summary>
      /// <returns>The text for the item</returns>
      public override string ToString()
      {
         if (this.textFormat.Length > 0)
         {
            return String.Format(textFormat, this.text);
         }
         else
         {
            return this.text.ToString();
         }
      }
      /// <summary>
      /// Default Constructor
      /// </summary>
      public SListItemBase()
      {
         this.text = (object)"";
      }
      public SListItemBase(
         object text
         ) : this()
      {
         this.text = text;
      }
      public SListItemBase(
         object text,
         int iconIndex
         ) : this(text)
      {
         this.iconIndex = iconIndex;
      }
      public SListItemBase(
         object text,
         int iconIndex,
         int indentation
         ) : this (text, iconIndex)
      {
         this.indentation = indentation;
      }
      public SListItemBase(
         object text,
         int iconIndex,
         int indentation,
         Color foreColor,
         Color backColor
         ) : this (text, iconIndex, indentation)
      {
         this.foreColor = foreColor;
         this.backColor = backColor;
      }
      public SListItemBase(
         object text,
         int iconIndex,
         int indentation,
         Color foreColor,
         Color backColor,
         Font font
         ) : this(text, iconIndex, indentation, foreColor, backColor)
      {
         this.font = font;
      }

      /// <summary>
      /// Disposes of any resources associated with the object
      /// </summary>
      public void Dispose()
      {
         if (this.stringFormat != null)
         {
            this.stringFormat.Dispose();
            this.stringFormat = null;
         }
      }
   }
   #endregion

   #region SListColumnCollection
   public class SListColumnCollection : ReadOnlyCollectionBase
   {
      private bool internalOrderSetting = false;
      private SListBox owner = null;

      public SListBox Owner
      {
         get
         {
            return this.owner;
         }
      }

      /// <summary>
      /// Gets the column at the specified index
      /// </summary>
      public SListColumn this[int index]
      {
         get
         {
            return (SListColumn)this.InnerList[index];
         }
      }

      /// <summary>
      /// Returns the column at the specified order,
      /// or null if no column.
      /// </summary>
      /// <param name="order">Order index</param>
      /// <returns>The SListColumn object for the column order
      /// specified, or null if nothing at the specified order
      /// </returns>
      public SListColumn ColumnAtOrder(int order)
      {
         SListColumn ret = null;
         foreach (SListColumn col in this.InnerList)
         {
            if (col.Order == order)
            {
               ret = col;
            }
         }
         return ret;
      }

      /// <summary>
      /// Removes the column at the specified index
      /// </summary>
      /// <param name="index">Index of column in control</param>
      public void Remove(int index)
      {
         SListColumn colToRemove = (SListColumn)this.InnerList[index];
         int order = colToRemove.Order;
         internalOrderSetting = true;
         foreach (SListColumn col in this.InnerList)
         {
            if (col.Order > order)
            {
               col.Order = col.Order - 1;
            }
         }
         internalOrderSetting = false;
         this.InnerList.RemoveAt(index);
      }

      /// <summary>
      /// Adds a column to the end of the column list
      /// with the default properties
      /// </summary>
      public void Add()
      {
         SListColumn col = new SListColumn();         
         col.Owner = this;
         Add(col);
      }
      
      /// <summary>
      /// Adds a column to the end of the column list
      /// </summary>
      /// <param name="column">Column to add</param>
      public void Add(SListColumn column)
      {
         column.Owner = this;
         internalOrderSetting = true;
         column.Order = this.InnerList.Count;
         internalOrderSetting = false;
         this.InnerList.Add(column);
         owner.OnColumnChanged(column);
      }

      /// <summary>
      /// Called when a column's width is changed
      /// </summary>
      /// <param name="item">The column whose width has changed</param>
      public virtual void OnColumnResize(
         SListColumn item
         )
      {
         if (this.owner != null)
         {
            this.owner.OnColumnChanged(item);
         }
      }

      /// <summary>
      /// Called when the SortOrder property of
      /// a column is changed
      /// </summary>
      /// <param name="item">Column whose sort order has been changed</param>
      public void SortByColumn(
         SListColumn item
         )
      {
         if (this.owner != null)
         {
            int colIndex = this.InnerList.IndexOf(item);
            this.owner.SortByColumn(colIndex);
         }
      }


      /// <summary>
      /// Internal method for returning the X position
      /// of a column.
      /// </summary>
      /// <param name="item">The column to get the X position for</param>
      /// <returns>X position of the column</returns>
      internal int ColumnX(
         SListColumn item
         )
      {
         int x = 0;
         int colOrder = item.Order;
         
         bool rightToLeft = false;         
         if (owner != null)
         {
            rightToLeft = (owner.RightToLeft == RightToLeft.Yes);
         }
         if (rightToLeft)
         {            
            x = owner.HorizontalExtent;
            if (owner.Width > x)
            {
               x = owner.Width;
            }
         }

         // All grouped columns are counted first against the edge:
         int myGroupOrder = item.GroupOrder;
         if (myGroupOrder <= 0)
         {
            myGroupOrder = 0x7FFFFFFF;
         }
         foreach(SListColumn col in this.InnerList)
         {
            if ((col.GroupOrder > 0) && (col.GroupOrder < myGroupOrder))
            {
               if (rightToLeft)
               {
                  x -= 16;
               }
               else
               {
                  x += 16;
               }
            }
         }
         
         if (item.GroupOrder <=0)
         {
            foreach(SListColumn col in this.InnerList)
            {
               if (col.Order < colOrder)
               {
                  if (rightToLeft)
                  {
                     x -= col.Width;
                  }
                  else
                  {
                     x += col.Width;
                  }
               }
            }
         }
         return x;
      }

      /// <summary>
      /// Internal method for modifying the order
      /// of a column.  Called when the Order property
      /// of an SListColumn is changed
      /// </summary>
      /// <param name="item">The item to change order</param>
      /// <param name="order">New order</param>
      internal int SetColumnOrder(
         SListColumn item,
         int order
         )
      {
         if (internalOrderSetting)
         {
            return order;
         }
         else
         {
            if (item.Order == order)
            {
               return item.Order;
            }
            else
            {
               internalOrderSetting = true;
               if (order > item.Order)
               {
                  // anything between item.Order and order should
                  // be reduced by 1:
                  foreach (SListColumn otherItem in this.InnerList)
                  {
                     if (otherItem.Order != item.Order)
                     {
                        if ((otherItem.Order >= item.Order) && (otherItem.Order
                         <= order ))
                        {
                           otherItem.Order = otherItem.Order - 1;
                        }
                     }
                  }
                  // set this item:
                  internalOrderSetting = false;
                  return order;
               }
               else
               {
                  // anything with an order >= order and <= item.Order should
                  // be increased by 1:
                  foreach (SListColumn otherItem in this.InnerList)
                  {
                     if (otherItem.Order != item.Order)
                     {
                        if ((otherItem.Order >= order) && (otherItem.Order <=
                         item.Order))
                        {
                           otherItem.Order = otherItem.Order + 1;
                        }
                     }
                  }                  
                  internalOrderSetting = false;
                  return order;
               }
            }
         }
      }

      internal void SetColumnGroupOrder(
         SListColumn item,
         int order
         )
      {
         owner.SetColumnGroupOrder(item ,order);
      }

      internal SListColumnCollection(
         SListBox owner)
      {
         this.owner = owner;
      }
   }
   #endregion

   #region SListColumn
   public class SListColumn
   {
      private string text = "";
      private object tag = null;
      private int order = 0;
      private int groupOrder = 0;
      private int width = 64;
      private SListColumnCollection owner;
      private SortOrder sortOrder;

      /// <summary>
      /// Gets/sets the owner collection for this column.
      /// Set automatically when the object is added to the
      /// SListColumCollection.
      /// </summary>
      [Description("Gets/sets the owner collection for this column.")]
      public SListColumnCollection Owner
      {
         get
         {
            return this.owner;
         }
         set
         {
            this.owner = value;
         }
      }

      /// <summary>
      /// Gets/sets the text for this column
      /// </summary>
      [Description("Gets/sets the text for this column.")]
      public string Text
      {
         get
         {
            return this.text;
         }
         set
         {
            this.text = value;
         }
      }

      /// <summary>
      /// Gets/sets an object associated with this column.
      /// </summary>
      [Description("Gets/sets an object associated with this column.")]
      public object Tag
      {
         get
         {
            return this.tag;
         }
         set
         {
            this.tag = value;
         }
      }

      /// <summary>
      /// Gets/sets the width of this column
      /// </summary>
      /// <exception cref="NotSupportedException">If you attempt to set the
       width whilst
      /// the column is grouped.</exception>
      [Description("Gets/sets the width of this column.")]
      public int Width
      {
         get
         {
            if ((this.groupOrder > 0) && (owner.Owner.ShowGroupingRows))
            {
               return 16;
            }
            else
            {
               return this.width;               
            }
         }
         set
         {
            this.width = value;
            if (owner != null)
            {
               owner.OnColumnResize(this);
            }
         }
      }

      /// <summary>
      /// Gets/sets the order at which this
      /// column is displayed in the ListBox.
      /// Only valid if the column is not grouped.
      /// </summary>
      /// <exception cref="ArgumentOutOfRangeException">If the order is out of
       bounds</exception>
      /// <exception cref="InvalidOperationException">If the column is not part
       of a control
      /// or the column is grouped.</exception>      
      [Description("Gets/sets the order at which this column is displayed in
       the ListBox.")]
      public int Order
      {
         get
         {
            return this.order;
         }
         set
         {
            if (owner == null)
            {
               throw new InvalidOperationException("Order cannot be set until
                the column is assigned to an SListBox control");
            }
            if ((this.order < 0) || (this.order > owner.Count))
            {
               throw new ArgumentOutOfRangeException("order", value, "Order
                must be greater than 0 and less than the number of columns.");
            }            
            this.order = owner.SetColumnOrder(this, value);
         }
      }

      /// <summary>
      /// Gets/sets the order at which this column
      /// is grouped in.
      /// If GroupOrder is &lt; 0, then the column is 
      /// not grouped.  If GroupOrder is &gt; 0 then
      /// the data within the column is grouped.
      /// </summary>
      /// <exception cref="InvalidOperationException">If the column is not part
       of a control.</exception>      
      [Description("Gets/sets the order at which this column is grouped in.  If
       GroupOrder is < 0 then the column is not grouped, otherwise it is.")]
      public int GroupOrder
      {
         get
         {
            return this.groupOrder;
         }
         set
         {
            if (this.owner == null)
            {
               throw new InvalidOperationException("GroupOrder cannot be set
                until the column is assigned to an SListBox control");
            }
            else
            {
               owner.SetColumnGroupOrder(this, value);
            }
         }
      }

      internal void SetColumnGroupOrder(int order)
      {
         this.groupOrder = order;
      }

      /// <summary>
      /// Gets the horizontal start position of this column
      /// </summary>
      /// <exception cref="InvalidOperationException">If the column is not part
       of a control</exception>
      [Description("Gets the horizontal start position of this column.")]
      public int X
      {
         get
         {
            if (owner == null)
            {
               throw new InvalidOperationException("X cannot be retrieved until
                the column is assigned to an SListBox control");
            }
            return owner.ColumnX(this);
         }
      }

      /// <summary>
      /// Returns a string representing this column.  If the text property is
       set,
      /// returns the text, otherwise returns "[Unnamed Column]"
      /// </summary>
      /// <returns>String representing this column.</returns>
      public override string ToString()
      {
         if (this.text.Length > 0)
         {
            return this.text;            
         }
         else
         {
            return "[Unnamed Column]";
         }
      }

      /// <summary>
      /// Gets/sets the sorting order of this column.
      /// </summary>
      /// <param name="sortOrder">Sort Order</param>
      public System.Windows.Forms.SortOrder SortOrder
      {
         get
         {
            return this.sortOrder;
         }
         set
         {
            this.sortOrder = value;
            owner.SortByColumn(this);
         }
      }

      /// <summary>
      /// Initializes a new SListColumn object
      /// </summary>
      public SListColumn()
      {
      }
   }
   #endregion

   #region SListColumnItem
   /// <summary>
   /// A single column item within the SListBox
   /// </summary>
   public class SListColumnItem : SListItemBase
   {
      public int CompareTo ( object obj, SortOrder sortOrder )
      {
         int ret = base.CompareTo(obj);
         if (sortOrder == SortOrder.Descending)
         {
            ret = -1 * ret;
         }
         return ret;
         
      }

      
      /// <summary>
      /// Draws this item.
      /// Used internally to draw the ListBox.
      /// </summary>
      /// <param name="graphics">Graphics object to draw to</param>
      /// <param name="imageList">Source of images</param>
      /// <param name="bounds">Bounding rectangle for the item</param>
      /// <param name="defaultFont">Font to use if the item does not have a
       font specified</param>
      /// <param name="selected">Whether the item should be rendered selected
       or not</param>
      /// <param name="rightToLeft">Whether item should be rendered
       right-to-left</param>
      public override void DrawItem(
         Graphics graphics,
         ImageList imageList,
         Rectangle bounds,
         Font defaultFont,
         bool selected,
         bool firstItem,
         bool rightToLeft
         )
      {
         base.DrawItem(
            graphics, 
            imageList, 
            bounds, 
            defaultFont, 
            selected,
            firstItem,
            rightToLeft);
      }
      public SListColumnItem() : base()
      {
         
      }
      public SListColumnItem(
         object text
         ) : base(text)
      {
      }
      public SListColumnItem(
         object text,
         int iconIndex
         ) : base(text, iconIndex)
      {
      }
      public SListColumnItem(
         object text,
         int iconIndex,
         int indentation
         ) : base(text, iconIndex, indentation)
      {
      }
      public SListColumnItem(
         object text,
         int iconIndex,
         int indentation,
         Color foreColor,
         Color backColor
         ) : base(text, iconIndex, indentation, foreColor, backColor)
      {
      }
      public SListColumnItem(
         object text,
         int iconIndex,
         int indentation,
         Color foreColor,
         Color backColor,
         Font font
         ) : base(text, iconIndex, indentation, foreColor, backColor, font)
      {
      }
   }
   #endregion

   #region SListItem
   /// <summary>
   /// An item within the SListBox.  Rows themselves
   /// can have associated text and data if desired, 
   /// and will be displayed instead of the columns
   /// if the ShowColumns property is set to False.
   /// </summary>
   public class SListItem : SListItemBase, ICollection
   {
      private ArrayList columns = null;
      private bool groupRow = false;
      private bool showColumns = false;
      private bool showText = true;
      private bool visible = true;
      private SListBox owner = null;

      /// <summary>
      /// Compares the current instance with another object of the same type.
      /// 
      /// The class attempts to use the CompareTo method of the text object
      /// for comparision.  If this is not available, the ToString() method 
      /// of this class is used for comparison.
      /// </summary>
      /// <param name="obj">An object to compare with this instance.</param>
      /// <returns>A 32-bit signed integer that indicates the relative order of
       the comparands.  
      /// The return value has these meanings:
      /// Less than zero - This instance is less than obj.  
      /// Zero - This instance is equal to obj.  
      /// Greater than zero  - This instance is greater than obj.</returns>
      public override int CompareTo( object obj )
      {
         if (owner == null)
         {
            return base.CompareTo(obj);
         }
         else
         {
            int[] sortColumns = owner.SortColumnArray();
            SortOrder[] order = owner.SortColumnOrder();

            int ret = 0;
            int index = 0;
            foreach (int sortColumn in sortColumns)
            {
               ret = this[sortColumn].CompareTo(((SListItem)obj)[sortColumn],
                order[index]);
               if (ret != 0)
               {
                  break;
               }
               index++;
            }
            return ret;
         }
      }

      /// <summary>
      /// Draws this item.
      /// Used internally to draw the ListBox.
      /// </summary>
      /// <param name="graphics">Graphics object to draw to</param>
      /// <param name="imageList">Source of images</param>
      /// <param name="bounds">Bounding rectangle for the item</param>
      /// <param name="defaultFont">Font to use if the item does not have a
       font specified</param>
      /// <param name="selected">Whether the item should be rendered selected
       or not</param>
      /// <param name="firstItem">Whether this item is the first item in the
       row</param>
      /// <param name="rightToLeft">Whether item should be rendered
       right-to-left</param>
      public override void DrawItem(
         Graphics graphics,
         ImageList imageList,
         Rectangle bounds,
         Font defaultFont,
         bool selected,
         bool firstItem,
         bool rightToLeft
         )
      {
         Rectangle groupedArea = bounds;
         groupedArea.Width = 0;
         bool drawGroupArea = false;
         Rectangle itemBounds = bounds;         
         if (this.showColumns)
         {
            int height = 0;
            if (owner != null)
            {
               int index = 0;
               foreach (SListColumn col in owner.Columns)
               {
                  if (index < this.columns.Count)
                  {
                     Rectangle columnBounds;
                     if (rightToLeft)
                     {
                        columnBounds = new Rectangle(
                           bounds.X + col.X  - col.Width, 
                           bounds.Y, 
                           col.Width, 
                           bounds.Height);
                     }
                     else
                     {
                        columnBounds = new Rectangle(
                           bounds.X + col.X, 
                           bounds.Y, 
                           col.Width, 
                           bounds.Height);
                     }
                     
                     SListColumnItem colItem = this[index];
                     columnBounds.Height  = colItem.Height;
                     if (columnBounds.Height > height)
                     {
                        height = columnBounds.Height;
                     }
                     if ((col.GroupOrder <=0) || (!owner.ShowGroupingRows))
                     {
                        colItem.DrawItem(
                           graphics,
                           imageList,
                           columnBounds,
                           defaultFont,
                           selected,
                           firstItem,
                           rightToLeft);
                     }
                     else
                     {
                        columnBounds.Y -= 1;
                        columnBounds.X -=1;
                        columnBounds.Height += 1;
                        columnBounds.Width += 1;
                        graphics.FillRectangle(SystemBrushes.Control,
                         columnBounds);
                        drawGroupArea = true;
                        if (columnBounds.X < groupedArea.X)
                        {
                           groupedArea.X = columnBounds.X;                     
                                 
                        }
                        if (columnBounds.Y < groupedArea.Y)
                        {
                           groupedArea.Y = columnBounds.Y;
                        }
                        groupedArea.Width += columnBounds.Width;
                        groupedArea.Height += columnBounds.Height;
                     }
                     
                     firstItem = false;
                  }
                  index++;
               }
               itemBounds.Y += height;
               itemBounds.Height -= height;
            }
         }
         
         if (drawGroupArea)
         {
            Rectangle groupBounds = itemBounds;
            groupBounds.Height += 1;
            if (rightToLeft)
            {
               groupBounds.X = itemBounds.X - groupedArea.Width;
               groupBounds.Width = groupedArea.Width;               
               itemBounds.X -= groupBounds.Width;
            }
            else
            {
               groupBounds.X = groupedArea.X;
               groupBounds.Width = groupedArea.Width;
               itemBounds.X += groupBounds.Width;
            }
            graphics.FillRectangle(SystemBrushes.Control, groupBounds);        
                
         }
         base.DrawItem(
            graphics,
            imageList,
            itemBounds,
            defaultFont,
            selected,
            true,
            rightToLeft);
      }

      /// <summary>
      /// Gets/sets the owning SListBox of this item.  Set 
      /// automatically when the item is added to an SListBox.
      /// The same SListItem cannot belong to two different
      /// SListBoxes.
      /// </summary>
      public SListBox Owner
      {
         get
         {
            return this.owner;
         }
         set
         {
            this.owner = value;
         }
      }

      /// <summary>
      /// Determines whether this item is expanded or not.       
      /// </summary>
      /// <returns></returns>
      public bool Expanded
      {
         get
         {
            bool expanded = true;
            if (owner != null)
            {
               expanded = owner.ItemIsExpanded(this);
            }   
            return expanded;
         }
         set
         {
            if (owner != null)
            {
               owner.ExpandCollapse(this, true);
            }
         }

      }

      /// <summary>
      /// Returns whether this row is a grouping row or not.       
      /// </summary>
      /// <returns></returns>
      public bool GroupRow
      {
         get
         {
            return this.groupRow;
         }
         set
         {
            this.groupRow = value;
         }
      }

      /// <summary>
      /// Expands (shows) any child items of this item. Child items are defined
      /// as items with a larger indentation.
      /// </summary>
      public void Expand()
      {
         if (owner != null)
         {
            owner.ExpandCollapse(this, true);
         }
      }

      /// <summary>
      /// Collapses (hides) any child items of this item. Child items are
       defined
      /// as items with a larger indentation.
      /// </summary>
      public void Collapse()
      {
         if (owner != null)
         {
            owner.ExpandCollapse(this, false);
         }
      }

      /// <summary>
      /// Gets the overall rendering height of this item in the
      /// list box
      /// </summary>
      public int RenderHeight
      {
         get
         {
            int renderHeight = 0;
            if (this.Visible)
            {
               if (this.showColumns)
               {
                  int maxColHeight = 0;
                  foreach (SListColumnItem colItem in this.columns)
                  {
                     if (colItem.Height > maxColHeight)
                     {
                        maxColHeight = colItem.Height;
                     }
                  }
                  renderHeight += maxColHeight;
               }
               if (this.showText)
               {
                  renderHeight += this.Height;
               }
            }
            return renderHeight;
         }
      }

      public bool Visible
      {
         get
         {
            return this.visible;
         }
         set
         {
            this.visible = value;
            OnHeightChanged();
         }
      }

      protected override void OnHeightChanged()
      {
         if (owner != null)
         {
            owner.OnHeightChanged(this);
         }
      }

      protected override void OnItemChanged()
      {
         if (owner != null)
         {
            owner.OnItemChanged(this);
         }
      }

      /// <summary>
      /// Gets/sets whether columns are displayed for this
      /// row.  If columns are not displayed, the Text, Icon
      /// etc properties of this class will be used for the
      /// display.  Defaults to true if the class is constructed
      /// with a series of columns, otherwise defaults to false.
      /// </summary>
      public bool ShowColumns
      {
         get
         {
            return this.showColumns;
         }
         set
         {
            this.showColumns = value;
            OnHeightChanged();
         }
      }

      /// <summary>
      /// Gets/sets whether the text for this row is displayed.
      /// Defaults to True.
      /// </summary>
      public bool ShowText
      {
         get
         {
            return this.showText;
         }
         set
         {            
            this.showText = value;
            OnHeightChanged();
         }
      }

      /// <summary>
      /// Returns the item at the specified column index
      /// </summary>
      public SListColumnItem this[int index]
      {
         get
         {
            return (SListColumnItem)this.columns[index];
         }
      }

      /// <summary>
      /// Gets the number of columns associated with this row 
      /// (if any)
      /// </summary>
      public int Count
      {
         get
         {
            return columns.Count;
         }
      }

      /// <summary>
      /// Gets a value indicating whether the columns collection
      /// is synchronized (thread-safe) or not.
      /// </summary>
      public bool IsSynchronized
      {
         get
         {
            return this.columns.IsSynchronized;
         }
      }
      /// <summary>
      /// Gets an object which can be used to synchronize access
      /// to the columns collection.
      /// </summary>
      public object SyncRoot
      {
         get
         {
            return this.columns.SyncRoot;
         }
      }
      /// <summary>
      /// Returns an enumerator for the columns in the collection
      /// </summary>
      /// <returns>An IEnumerator object for the columns in the item</returns>
      public System.Collections.IEnumerator GetEnumerator()
      {
         return this.columns.GetEnumerator();
      }
      /// <summary>
      /// Copies a range of items from the columns collection to
      /// a compatible one-dimensional array.
      /// </summary>
      /// <param name="array">Array to copy to</param>
      /// <param name="index">Index to start at</param>
      public void CopyTo(System.Array array, int index)
      {
         this.columns.CopyTo(array, index);
      }

      private void init()
      {
         this.columns = new ArrayList();
      }
      private void init(
         SListColumnItem[] columns
         )
      {
         init();
         foreach (SListColumnItem column in columns)
         {
            this.columns.Add(column);
         }
      }
      /// <summary>
      ///  Constructs a new the SListItem object
      /// </summary>
      public SListItem() : base()
      {
         this.columns = new ArrayList();
      }
      public SListItem(
         SListColumnItem[] columnItems
         ) : base()
      {
         init(columnItems);
         this.showColumns = true;
      }
      public SListItem(
         SListColumnItem[] columnItems,
         object text
         ) : base(text, -1, 0)
      {
         init(columnItems);
         this.showColumns = true;
      }
      public SListItem(
         SListColumnItem[] columnItems,
         object text,
         int indentation
         ) : base(text, -1, indentation)
      {
         init(columnItems);
         this.showColumns = true;
      }
      public SListItem(
         SListColumnItem[] columnItems,
         object text,
         int indentation,
         int iconIndex
         ) : base(text, iconIndex, indentation)
      {
         init(columnItems);
         this.showColumns = true;
      }
      public SListItem(
         object text
         ) : base(text)
      {
         init();
      }
      public SListItem(
         object text,
         int iconIndex
         ) : base(text, iconIndex)
      {
         init();
      }
      public SListItem(
         object text,
         int iconIndex,
         int indentation
         ) : base(text, iconIndex, indentation)
      {
         init();
      }
      public SListItem(
         object text,
         int iconIndex,
         int indentation,
         Color foreColor,
         Color backColor
         ) : base(text, iconIndex, indentation, foreColor, backColor)
      {
         init();
      }
      public SListItem(
         object text,
         int iconIndex,
         int indentation,
         Color foreColor,
         Color backColor,
         Font font
         ) : base(text, iconIndex, indentation, foreColor, backColor, font)
      {
         init();
      }
   }
   #endregion
}