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

SourceGrid - Open Source C# Grid Control

Rate me:
Please Sign up or sign in to vote.
4.94/5 (429 votes)
4 Aug 2013MIT24 min read 5M   23.7K   1K  
SourceGrid is a free open source grid control. Supports virtual grid, custom cells and editors, advanced formatting options and many others features
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;

namespace SourceGrid2
{
	/// <summary>
	/// Column Information
	/// </summary>
	public class ColumnInfo
	{
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="p_Grid"></param>
		private ColumnInfo(GridVirtual p_Grid)
		{
			m_Grid = p_Grid;
		}

		private int m_Width = Utility.DefaultCellWidth;
		/// <summary>
		/// Width of the current Column
		/// </summary>
		public int Width
		{
			get{return m_Width;}
			set
			{
				if (value<=0)
					value=1;

				if (m_Width != value)
				{
					m_Width = value;
					if (m_Grid!=null)
						m_Grid.Columns.InvokeColumnWidthChanged(new ColumnInfoEventArgs(this));
				}
			}
		}

		private int m_Left;
		/// <summary>
		/// Left absolute position of the current Column
		/// </summary>
		public int Left
		{
			get{return m_Left;}
		}

		/// <summary>
		/// Right of the column (Left+Width)
		/// </summary>
		public int Right
		{
			get{return Left+Width;}
		}
		//private int m_Index;
		/// <summary>
		/// Index of the current Column
		/// </summary>
		public int Index
		{
			get{return m_Grid.Columns.IndexOf(this);}
		}

		private GridVirtual m_Grid;
		/// <summary>
		/// Attached Grid
		/// </summary>
		public GridVirtual Grid
		{
			get{return m_Grid;}
		}


		/// <summary>
		/// Returns all the cells at current column position
		/// </summary>
		/// <returns></returns>
		public Cells.ICellVirtual[] GetCells()
		{
			if (m_Grid == null)
				throw new SourceGridException("Invalid Grid object");

			int l_CurrentCol = Index;

			Cells.ICellVirtual[] l_Cells = new Cells.ICellVirtual[m_Grid.Rows.Count];
			for (int r = 0; r < m_Grid.Rows.Count; r++)
				l_Cells[r] = m_Grid.GetCell(r, l_CurrentCol);

			return l_Cells;
		}

		/// <summary>
		/// Set the specified cells at the current row position
		/// </summary>
		/// <param name="p_Cells"></param>
		public void SetCells(params Cells.ICellVirtual[] p_Cells)
		{
			if (m_Grid == null)
				throw new SourceGridException("Invalid Grid object");

			if (p_Cells!=null)
			{
				int l_CurrentCol = Index;

				for (int r = 0; r < p_Cells.Length; r++)
					m_Grid.SetCell(r, l_CurrentCol, p_Cells[r]);
			}
		}

		/// <summary>
		/// Move the Focus to the first cell not fixed of the current row
		/// </summary>
		/// <returns></returns>
		public bool Focus()
		{
			if (Grid.ColumnsCount > Index && Grid.RowsCount > Grid.FixedRows)
				return Grid.SetFocusCell(new Position(Grid.FixedRows, Index));
			else
				return false;
		}

		/// <summary>
		/// Gets or sets if the current row is selected. If only a column of the row is selected this property returns true.
		/// </summary>
		public bool Select
		{
			get{return Grid.Selection.ContainsColumn(Index);}
			set
			{
				if (Grid.ColumnsCount > Index && Grid.RowsCount > 0)
				{
					if (value)
						Grid.Selection.AddRange(new Range(0, Index, Grid.RowsCount-1, Index));
					else
						Grid.Selection.RemoveRange(new Range(0, Index, Grid.RowsCount-1, Index));
				}
			}
		}


		private object m_Tag;
		/// <summary>
		/// A property that the user can use to insert custom informations associated to a specific column
		/// </summary>
		public object Tag
		{
			get{return m_Tag;}
			set{m_Tag = value;}
		}

		private AutoSizeMode m_AutoSizeMode = AutoSizeMode.EnableAutoSize | AutoSizeMode.EnableStretch;
		/// <summary>
		/// Flags for autosize and stretch
		/// </summary>
		public AutoSizeMode AutoSizeMode
		{
			get{return m_AutoSizeMode;}
			set{m_AutoSizeMode = value;}
		}

		#region ColumnInfoCollection
		/// <summary>
		/// Collection of ColumnInfo
		/// </summary>
		public class ColumnInfoCollection : ICollection
		{
			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="p_grid"></param>
			public ColumnInfoCollection(GridVirtual p_grid)
			{
				m_Grid = p_grid;
			}

			private GridVirtual m_Grid;
			/// <summary>
			/// Attached Grid
			/// </summary>
			public GridVirtual Grid
			{
				get{return m_Grid;}
			}

			private ArrayList m_List = new ArrayList();
			#region Comparer
			private ColumnInfoLeftComparer m_Comparer = new ColumnInfoLeftComparer();
			public class ColumnInfoLeftComparer : IComparer
			{
				public System.Int32 Compare ( System.Object x , System.Object y )
				{
					return ((ColumnInfo)x).Left.CompareTo( ((ColumnInfo)y).Left);
				}
			}
			#endregion

			/// <summary>
			/// Calculate the Column that have the Left value smaller or equal than the point p_X, or -1 if not found found. ExactMatch = false
			/// </summary>
			/// <param name="p_X">Absolute point to search</param>
			/// <returns></returns>
			public int ColumnAtPoint(int p_X)
			{
				return ColumnAtPoint(p_X, false);
			}
			/// <summary>
			/// Calculate the Column that have the Left value smaller or equal than the point p_X, or -1 if not found found.
			/// </summary>
			/// <param name="p_X">X Coordinate to search for a column</param>
			/// <param name="p_ExactMatch">True to returns only exact position. For example if you use a point outside the range and this value is true no column is returned otherwise the nearest column is returned.</param>
			/// <returns></returns>
			public int ColumnAtPoint(int p_X,bool p_ExactMatch)
			{
				//Restituisce la righa con il Left uguale a quello passato o la righa con il Left minore a quallo passato.
				// o -1 se tutte le righe hanno il Left maggiore
				int l_IndexFound;

				ColumnInfo l_Find = new ColumnInfo(null);
				l_Find.m_Left = p_X;
				int l_ObjFound = m_List.BinarySearch(l_Find,m_Comparer);
				if (l_ObjFound>=0) //trovato il valore uguale
					l_IndexFound = l_ObjFound;
				else
				{
					l_ObjFound = ~l_ObjFound; //bitwise operator to return the nearest index
					if (l_ObjFound<=0)
						l_IndexFound = -1; //nessuna righa compatibile
					else if (l_ObjFound <= m_List.Count)
						l_IndexFound = l_ObjFound-1; //trovata una righa compatibile
					else
						l_IndexFound = -1; //non dovrebbe mai capitare
				}

				//se � stato richiesto un exactMatch verifico che il punto sia compreso tra il minimo e il massimo
				if (p_ExactMatch && l_IndexFound>=0)
				{
					if (p_X > Right ||
						p_X < Left)
						l_IndexFound = -1;
				}

				return l_IndexFound;
			}


			/// <summary>
			/// Returns true if the range passed is valid
			/// </summary>
			/// <param name="p_StartIndex"></param>
			/// <param name="p_Count"></param>
			/// <returns></returns>
			public bool IsValidRange(int p_StartIndex, int p_Count)
			{
				if (p_StartIndex < Count && p_StartIndex >= 0 &&
					p_Count > 0 && (p_StartIndex+p_Count) <= Count)
					return true;
				else
					return false;
			}

			/// <summary>
			/// Returns true if the range passed is valid for insert method
			/// </summary>
			/// <param name="p_StartIndex"></param>
			/// <param name="p_Count"></param>
			/// <returns></returns>
			public bool IsValidRangeForInsert(int p_StartIndex, int p_Count)
			{
				if (p_StartIndex <= Count && p_StartIndex >= 0 &&
					p_Count > 0)
					return true;
				else
					return false;
			}

			#region Insert/Remove Methods

			/// <summary>
			/// Insert a column at the specified position using the specified cells
			/// </summary>
			/// <param name="p_Index"></param>
			/// <param name="p_Cells">The new column values</param>
			public void Insert(int p_Index, params Cells.ICellVirtual[] p_Cells)
			{
				Insert(p_Index);

				this[p_Index].SetCells(p_Cells);
			}

			/// <summary>
			/// Insert a column at the specified position
			/// </summary>
			/// <param name="p_Index"></param>
			public void Insert(int p_Index)
			{
				InsertRange(p_Index, 1);
			}

			/// <summary>
			/// Remove a column at the speicifed position
			/// </summary>
			/// <param name="p_Index"></param>
			public void Remove(int p_Index)
			{
				RemoveRange(p_Index, 1);
			}

			/// <summary>
			/// Insert the specified number of Columns at the specified position
			/// </summary>
			/// <param name="p_StartIndex"></param>
			/// <param name="p_Count"></param>
			public void InsertRange(int p_StartIndex, int p_Count)
			{
				if (IsValidRangeForInsert(p_StartIndex, p_Count)==false)
					throw new SourceGridException("Invalid index");

				//TODO si potrebbe ottimizzare aumentando la capacity
				for (int c = 0; c < p_Count; c++)
				{
					m_List.Insert(p_StartIndex+c,new ColumnInfo(m_Grid));
				}

				if (AutoCalculateLeft)
					CalculateLeft(p_StartIndex);

				OnColumnsAdded(new IndexRangeEventArgs(p_StartIndex, p_Count));
			}

			/// <summary>
			/// Remove the ColumnInfo at the specified positions
			/// </summary>
			/// <param name="p_StartIndex"></param>
			/// <param name="p_Count"></param>
			public void RemoveRange(int p_StartIndex, int p_Count)
			{
				if (IsValidRange(p_StartIndex, p_Count)==false)
					throw new SourceGridException("Invalid index");

				//azzero le informazioni legate alla griglia
				for (int c = p_StartIndex; c < p_StartIndex+p_Count; c++)
				{
					this[c].m_Grid = null;
					//this[c].m_Index = -1;
				}

				m_List.RemoveRange(p_StartIndex, p_Count);

				if (AutoCalculateLeft)
					CalculateLeft(p_StartIndex);

				OnColumnsRemoved(new IndexRangeEventArgs(p_StartIndex, p_Count));
			}


			#endregion

			/// <summary>
			/// Move a column from one position to another position
			/// </summary>
			/// <param name="p_CurrentColumnPosition"></param>
			/// <param name="p_NewColumnPosition"></param>
			public void Move(int p_CurrentColumnPosition, int p_NewColumnPosition)
			{
				if (p_CurrentColumnPosition == p_NewColumnPosition)
					return;

				int l_ColumnMin, l_ColumnMax;
				if (p_CurrentColumnPosition < p_NewColumnPosition)
				{
					l_ColumnMin = p_CurrentColumnPosition;
					l_ColumnMax = p_NewColumnPosition;
				}
				else
				{
					l_ColumnMin = p_NewColumnPosition;
					l_ColumnMax = p_CurrentColumnPosition;
				}

				for (int r = l_ColumnMin; r < l_ColumnMax; r++)
				{
					Swap(r, r + 1);
				}
			}

			/// <summary>
			/// Change the position of column 1 with column 2.
			/// </summary>
			/// <param name="p_ColumnIndex1"></param>
			/// <param name="p_ColumnIndex2"></param>
			public void Swap(int p_ColumnIndex1, int p_ColumnIndex2)
			{
				if (p_ColumnIndex1 == p_ColumnIndex2)
					return;

				ColumnInfo l_Column1 = this[p_ColumnIndex1];
				Cells.ICellVirtual[] l_Cells1 = l_Column1.GetCells();
				ColumnInfo l_Column2 = this[p_ColumnIndex2];
				Cells.ICellVirtual[] l_Cells2 = l_Column2.GetCells();

				m_List[p_ColumnIndex1] = l_Column2;
				m_List[p_ColumnIndex2] = l_Column1;

				l_Column1.SetCells(new Cells.ICellVirtual[l_Cells1.Length]);
				l_Column2.SetCells(new Cells.ICellVirtual[l_Cells1.Length]);
				l_Column1.SetCells(l_Cells1);
				l_Column2.SetCells(l_Cells2);

				if (AutoCalculateLeft)
					CalculateLeft(0);
			}

			/// <summary>
			/// Fired when the number of columns change
			/// </summary>
			public event IndexRangeEventHandler ColumnsAdded;

			/// <summary>
			/// Fired when the number of columns change
			/// </summary>
			/// <param name="e"></param>
			protected virtual void OnColumnsAdded(IndexRangeEventArgs e)
			{
				if (ColumnsAdded!=null)
					ColumnsAdded(this, e);
			}

			/// <summary>
			/// Fired when the number of columns change
			/// </summary>
			public event IndexRangeEventHandler ColumnsRemoved;

			/// <summary>
			/// Fired when the number of columns change
			/// </summary>
			/// <param name="e"></param>
			protected virtual void OnColumnsRemoved(IndexRangeEventArgs e)
			{
				if (ColumnsRemoved!=null)
					ColumnsRemoved(this, e);
			}

			/// <summary>
			/// Indexer. Returns a ColumnInfo at the specified position
			/// </summary>
			public ColumnInfo this[int p]
			{
				get{return (ColumnInfo)m_List[p];}
			}

			/// <summary>
			/// Recalculate all the Left positions from the specified index
			/// </summary>
			/// <param name="p_StartIndex"></param>
			public void CalculateLeft(int p_StartIndex)
			{
				if (Count > 0)
				{
					int l_CurrentLeft = 0;
					if (p_StartIndex != 0)
						l_CurrentLeft = this[p_StartIndex-1].Left+this[p_StartIndex-1].Width;

					for (int c = p_StartIndex; c < Count; c++)
					{
						this[c].m_Left = l_CurrentLeft;
						l_CurrentLeft += this[c].m_Width;
					}
				}
			}

			/// <summary>
			/// Returns the maximum right value of the columns. Calculated with Columns[lastCol].Right or 0 if no columns are presents.
			/// </summary>
			public int Right
			{
				get
				{
					if (Count <= 0)
						return 0;
					else
						return this[Count-1].Right;
				}
			}
			/// <summary>
			/// Returns the minimum left value of the columns. Calculated with Columns[0].Left or 0 if no columns are presents.
			/// </summary>
			public int Left
			{
				get
				{
					if (Count <= 0)
						return 0;
					else
						return this[0].Left;
				}
			}
			/// <summary>
			/// Fired when the user change the Width property of one of the Column
			/// </summary>
			public event ColumnInfoEventHandler ColumnWidthChanged;

			/// <summary>
			/// Execute the RowHeightChanged event
			/// </summary>
			/// <param name="e"></param>
			public void InvokeColumnWidthChanged(ColumnInfoEventArgs e)
			{
				if (AutoCalculateLeft)
					CalculateLeft(e.Column.Index);

				if (ColumnWidthChanged!=null)
					ColumnWidthChanged(this, e);
			}

			private bool m_bAutoCalculateLeft = true;
			/// <summary>
			/// Indicates if auto recalculate left position when width value change. Default = true. Can be used when you need to change many Width value for example for an AutoSize operation to increase performance.
			/// </summary>
			public bool AutoCalculateLeft
			{
				get{return m_bAutoCalculateLeft;}
				set
				{
					m_bAutoCalculateLeft = value;
					if (m_bAutoCalculateLeft)
						CalculateLeft(0);
				}
			}

			public int IndexOf(ColumnInfo p_Info)
			{
				return m_List.IndexOf(p_Info);
			}

			#region ICollection
			public virtual void CopyTo ( System.Array array , System.Int32 index )
			{
				m_List.CopyTo(array,index);
			}
			public int Count
			{
				get{return m_List.Count;}
			}
			public bool IsSynchronized
			{
				get{return m_List.IsSynchronized;}
			}
			public object SyncRoot
			{
				get{return m_List.SyncRoot;}
			}
			public virtual System.Collections.IEnumerator GetEnumerator (  )
			{
				return m_List.GetEnumerator();
			}
			#endregion
		}

		#endregion
	}
}

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 MIT License


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

Comments and Discussions