Click here to Skip to main content
15,895,084 members
Articles / Programming Languages / C#

DataGridView with hierarchical data binding

,
Rate me:
Please Sign up or sign in to vote.
4.77/5 (22 votes)
31 Jul 2008Ms-PL2 min read 234.9K   15.9K   87  
The TreeGridView by Mark Rideout with data binding and sorting
//---------------------------------------------------------------------
// 
//  Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections.Generic;
using System.Diagnostics;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Drawing.Design;
using System.Text;

namespace HierarchicalGrid
{
	[
		ToolboxItem(false),
		DesignTimeVisible(false)
	]
    public class HierarchicalGridNode : DataGridViewRow//, IComponent
    {
		internal HierarchicalGridView _grid;
		internal HierarchicalGridNode _parent;
		internal HierarchicalGridNodeCollection _owner;
        internal bool IsExpanded;
		internal bool IsRoot;
		internal bool _isSited;
		internal bool _isFirstSibling;
		internal bool _isLastSibling;
		internal Image _image;
		internal int _imageIndex;

		private Random rndSeed = new Random();
		public int UniqueValue = -1;
        TreeGridCell _treeCell;
        HierarchicalGridNodeCollection childrenNodes;

		private int _index;
		private int _level;
		private bool childCellsCreated = false;

		// needed for IComponent
		private ISite site = null;
		private EventHandler disposed = null;

		internal HierarchicalGridNode(HierarchicalGridView owner)
			: this()
		{
			this._grid = owner;
			this.IsExpanded = true;
		}

        public HierarchicalGridNode()
        {            
			_index = -1;
			_level = -1;            
            IsExpanded = false;
			UniqueValue = this.rndSeed.Next();
			_isSited = false;
			_isFirstSibling = false;
			_isLastSibling = false;
			_imageIndex = -1;
		}

		public override object Clone()
		{
			HierarchicalGridNode r = (HierarchicalGridNode)base.Clone();
			r.UniqueValue = -1;
			r._level = this._level;
			r._grid = this._grid;
			r._parent = this.Parent;

			r._imageIndex = this._imageIndex;
			if (r._imageIndex == -1)
				r.Image = this.Image;

			r.IsExpanded = this.IsExpanded;
			//r.treeCell = new TreeGridCell();

			return r;
		}
		
		internal protected virtual void UnSited()
		{
			// This row is being removed from being displayed on the grid.
			TreeGridCell cell;
			foreach (DataGridViewCell DGVcell in this.Cells)
			{
				cell = DGVcell as TreeGridCell;
				if (cell != null)
				{
					cell.UnSited();
				}
			}
			this._isSited = false;
		}

		internal protected virtual void Sited()
		{
			// This row is being added to the grid.
			this._isSited = true;
			this.childCellsCreated = true;
			Debug.Assert(this._grid != null);

			TreeGridCell cell;
			foreach (DataGridViewCell DGVcell in this.Cells)
			{
				cell = DGVcell as TreeGridCell;
				if (cell != null)
				{
					cell.Sited();// Level = this.Level;
				}
			}

		}

		// Represents the index of this row in the Grid
		[System.ComponentModel.Description("Represents the index of this row in the Grid. Advanced usage."),
		System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced),
		 Browsable(false),
		 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public int RowIndex{
			get{
				return base.Index;
			}
		}

		// Represents the index of this row based upon its position in the collection.
		[Browsable(false),
		 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public new int Index
		{
			get
			{
				if (_index == -1)
				{
					// get the index from the collection if unknown
					_index = this._owner.IndexOf(this);
				}

				return _index;
			}
			internal set
			{
				_index = value;
			}
		}

        [Browsable(false),
        EditorBrowsable( EditorBrowsableState.Never), 
        DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)]
        public ImageList ImageList
        {
            get
            {
                if (this._grid != null)
                    return this._grid.ImageList;
                else
                    return null;
            }
        }

		private bool ShouldSerializeImageIndex()
		{
			return (this._imageIndex != -1 && this._image == null);
		}

        [Category("Appearance"),
        Description("..."), DefaultValue(-1),
        TypeConverter(typeof(ImageIndexConverter)),
        Editor("System.Windows.Forms.Design.ImageIndexEditor", typeof(UITypeEditor))]
		public int ImageIndex
		{
			get { return _imageIndex; }
			set
			{
				_imageIndex = value;
				if (_imageIndex != -1)
				{
					// when a imageIndex is provided we do not store the image.
					this._image = null;
				}
				if (this._isSited)
				{
					// when the image changes the cell's style must be updated
					this._treeCell.UpdateStyle();
					if (this.Displayed)
						this._grid.InvalidateRow(this.RowIndex);
				}
			}
		}

		private bool ShouldSerializeImage()
		{
			return (this._imageIndex == -1 && this._image != null);
		}

		public Image Image
		{
			get {
				if (_image == null && _imageIndex != -1)
				{
					if (this.ImageList != null && this._imageIndex < this.ImageList.Images.Count)
					{
						// get image from image index
						return this.ImageList.Images[this._imageIndex];
					}
					else
						return null;
				}
				else
				{
					// image from image property
					return this._image;
				};
			}
			set
			{
				_image = value;
				if (_image != null)
				{
					// when a image is provided we do not store the imageIndex.
					this._imageIndex = -1;
				}
				if (this._isSited)
				{
					// when the image changes the cell's style must be updated
					this._treeCell.UpdateStyle();
					if (this.Displayed)
						this._grid.InvalidateRow(this.RowIndex);
				}
			}
		}

		protected override DataGridViewCellCollection CreateCellsInstance()
		{
			DataGridViewCellCollection cells = base.CreateCellsInstance();
			cells.CollectionChanged += cells_CollectionChanged;
			return cells;
		}

		void cells_CollectionChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e)
		{
			// Exit if there already is a tree cell for this row
			if (_treeCell != null) return;

			if (e.Action == System.ComponentModel.CollectionChangeAction.Add || e.Action == System.ComponentModel.CollectionChangeAction.Refresh)
			{
				TreeGridCell treeCell = null;

				if (e.Element == null)
				{
					foreach (DataGridViewCell cell in base.Cells)
					{
						if (cell.GetType().IsAssignableFrom(typeof(TreeGridCell)))
						{
							treeCell = (TreeGridCell)cell;
							break;
						}

					}
				}
				else
				{
					treeCell = e.Element as TreeGridCell;
				}

				if (treeCell != null) 
				  _treeCell = treeCell;
			}
		}

		[Category("Data"),
		 Description("The collection of root nodes in the treelist."),
		 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		 Editor(typeof(CollectionEditor), typeof(UITypeEditor))]
        public HierarchicalGridNodeCollection Nodes
        {
            get
            {
                if (childrenNodes == null)
                {
                    childrenNodes = new HierarchicalGridNodeCollection(this);
                }
                return childrenNodes;
            }
            set { ;}
        }

		// Create a new Cell property because by default a row is not in the grid and won't
		// have any cells. We have to fabricate the cell collection ourself.
        [Browsable(false),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new DataGridViewCellCollection Cells
		{
			get
			{
				if (!childCellsCreated && this.DataGridView == null)
				{
                    if (this._grid == null) return null;

					this.CreateCells(this._grid);
					childCellsCreated = true;
				}
				return base.Cells;
			}
		}

		[Browsable(false),
		 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public int Level
		{
			get {
				if (this._level == -1)
				{
					// calculate level
					int walk = 0;
					HierarchicalGridNode walkRow = this.Parent;
					while (walkRow != null)
					{
						walk++;
						walkRow = walkRow.Parent;
					}
					this._level = walk;
				}
				return this._level; }
		}

		[Browsable(false),
		 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public HierarchicalGridNode Parent
		{
			get
			{
				return this._parent;
			}
		}

		[Browsable(false),
		 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public virtual bool HasChildren
		{
			get
			{
				return (this.childrenNodes != null && this.Nodes.Count != 0);
			}
		}

        [Browsable(false)]
        public bool IsSited
        {
            get
            {
                return this._isSited;
            }
        }
		[Browsable(false)]
		public bool IsFirstSibling
		{
			get
			{
				return (this.Index == 0);
			}
		}

		[Browsable(false)]
		public bool IsLastSibling
		{
			get
			{
				HierarchicalGridNode parent = this.Parent;
				if (parent != null && parent.HasChildren)
				{
					return (this.Index == parent.Nodes.Count - 1);
				}
				else
					return true;
			}
		}
		
		public virtual bool Collapse()
		{
			return this._grid.CollapseNode(this);
		}

		public virtual bool Expand()
		{
			if (this._grid != null)
				return this._grid.ExpandNode(this);
			else
			{
				this.IsExpanded = true;
				return true;
			}
		}

		internal protected virtual bool InsertChildNode(int index, HierarchicalGridNode node)
		{
			node._parent = this;
			node._grid = this._grid;

            // ensure that all children of this node has their grid set
            if (this._grid != null)
                UpdateChildNodes(node);

			//TODO: do we need to use index parameter?
			if ((this._isSited || this.IsRoot) && this.IsExpanded)
				this._grid.SiteNode(node);
			return true;
		}

		internal protected virtual bool InsertChildNodes(int index, params HierarchicalGridNode[] nodes)
		{
			foreach (HierarchicalGridNode node in nodes)
			{
				this.InsertChildNode(index, node);
			}
			return true;
		}

		internal protected virtual bool AddChildNode(HierarchicalGridNode node)
		{
			node._parent = this;
			node._grid = this._grid;

            // ensure that all children of this node has their grid set
            if (this._grid != null)
                UpdateChildNodes(node);

			if ((this._isSited || this.IsRoot) && this.IsExpanded && !node._isSited)
				this._grid.SiteNode(node);

			return true;
		}
		internal protected virtual bool AddChildNodes(params HierarchicalGridNode[] nodes)
		{
			//TODO: Convert the final call into an SiteNodes??
			foreach (HierarchicalGridNode node in nodes)
			{
				this.AddChildNode(node);
			}
			return true;

		}

		internal protected virtual bool RemoveChildNode(HierarchicalGridNode node)
		{
			if ((this.IsRoot || this._isSited) && this.IsExpanded )
			{
				//We only unsite out child node if we are sited and expanded.
				this._grid.UnSiteNode(node);
			
			}
            node._grid = null;	
			node._parent = null;
			return true;

		}

		internal protected virtual bool ClearNodes()
		{
            if (this.HasChildren)
            {
                for (int i = this.Nodes.Count - 1; i >= 0; i--)
                {
                    this.Nodes.RemoveAt(i);
                }
            }
			return true;
		}

        [
            Browsable(false),
            EditorBrowsable(EditorBrowsableState.Advanced)
        ]
        public event EventHandler Disposed
        {
            add
            {
                this.disposed += value;
            }
            remove
            {
                this.disposed -= value;
            }
        }

		[
			Browsable(false),
			DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
		]
		public ISite Site
		{
			get
			{
				return this.site;
			}
			set
			{
				this.site = value;
			}
		}

        private void UpdateChildNodes(HierarchicalGridNode node)
        {
            if (node.HasChildren)
            {
                foreach (HierarchicalGridNode childNode in node.Nodes)
                {
                    childNode._grid = node._grid;
                    this.UpdateChildNodes(childNode);
                }
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder(36);
            sb.Append("TreeGridNode { Index=");
            sb.Append(this.RowIndex.ToString(System.Globalization.CultureInfo.CurrentCulture));
            sb.Append(" }");
            return sb.ToString();
        }

        /*protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle rowBounds, int rowIndex, DataGridViewElementStates rowState, bool isFirstDisplayedRow, bool isLastVisibleRow)
        {
            Brush brush = new SolidBrush(Color.DarkGoldenrod);
            graphics.FillRectangle(brush, rowBounds.Left, rowBounds.Bottom - 2, this.DataGridView.Width, 2);
            brush.Dispose();
            base.Paint(graphics, clipBounds, rowBounds, rowIndex, rowState, isFirstDisplayedRow, isLastVisibleRow);
        }*/

		//protected override void Dispose(bool disposing) {
		//    if (disposing)
		//    {
		//        lock(this)
		//        {
		//            if (this.site != null && this.site.Container != null)
		//            {
		//                this.site.Container.Remove(this);
		//            }

		//            if (this.disposed != null)
		//            {
		//                this.disposed(this, EventArgs.Empty);
		//            }
		//        }
		//    }

		//    base.Dispose(disposing);
		//}
	}

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Engineer
Brazil Brazil
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Written By
Brazil Brazil
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions