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

DataGrid Zen Novice

Rate me:
Please Sign up or sign in to vote.
4.90/5 (103 votes)
14 Jan 200418 min read 459.7K   2.7K   233  
Add style to DataGrid columns, custom columns.
using System;
using System.Data;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;

namespace ComboBoxTest
{
	/// <summary>
	/// This DataGrid Column class implements a ComboBox Column. The combo box is fed from a 
	/// table containing an collection of value objects and a string descriptor (the displayed 
	/// element). On losing focus, the combo box the current cell in the associated DataGrid
	/// is updated with the current selected value object
	/// </summary>
	public class MyComboColumn : System.Windows.Forms.DataGridTextBoxColumn 
	{
		// each column shares a single combobox
		private ComboBox _cboColumn;
		// data we save when the column is instantiated (no provision for subsequent rebinds)
		private object _objSource;
		private string _strMember;
		private string _strValue;
		// remember if we have bound the combobox to the parent datagrid control
		private bool _bIsComboBound = false;
		// data that describes the background and foreground colors used to paint the cell when not in edit mode
		private Brush _backBrush = null;
		private Brush _foreBrush = null;

		// information picked up and held when we start to edit the source table
		private int _iRowNum;
		private CurrencyManager _cmSource;

		/// <summary>
		/// initialize the combobox column and take note of the data source/member/value used to fill the combobox
		/// </summary>
		/// <param name="objSource">bind Source for the combobox (typical is a DataTable object)</param>
		/// <param name="strMember">bind for the combobox DisplayMember (typical is a Column Name within the Source)</param>
		/// <param name="strValue">bind for the combobox ValueMember (typical is a Column Name within the Source)</param>
		public MyComboColumn(object objSource, string strMember, string strValue, bool bUseDropDownList)
		{
			_objSource = objSource;
			_strMember = strMember;
			_strValue = strValue;

			// create a new combobox object
			_cboColumn = new ComboBox();
			// set the data link to the source, member and value displayed by this combobox
			_cboColumn.DataSource = _objSource;
			_cboColumn.DisplayMember = _strMember;
			_cboColumn.ValueMember = _strValue;
			if (bUseDropDownList == true) 
			{
				// we cannot create new countries through this column so disallow editing by making the combo a drop-down list
				_cboColumn.DropDownStyle = ComboBoxStyle.DropDownList;
				// Setting ReadOnly changes the behavior of the column so the 'leave' event fires whenever we 
				// change cell. The default behavior will not fire the 'leave' event when we up-arrow or 
				// down-arrow to the next row.
				this.ReadOnly = true;
			} 
			else 
			{
				// because this is not an edit-through combo we are going to suppress key-strokes.
				// notice this routine does not affect navigation or delete keys. The delete key
				// allows us to select the null data value for this row
				_cboColumn.KeyPress +=new KeyPressEventHandler(_cboColumn_KeyPress);
			}
			// we need to know when the combo box is getting closed so we can update the source data and
			// hide the combobox control
			_cboColumn.Leave += new EventHandler(cboColumn_Leave);
			// make sure the combobox is invisible until we've set its correct position and dimensions
			_cboColumn.Visible = false;
		}

		private void _cboColumn_KeyPress(object sender, KeyPressEventArgs e)
		{
			// mark all key events as handled to block editing of combobox entries
			e.Handled = true;
		}

		protected override void Edit(CurrencyManager source, int rowNum, Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible)
		{
			// the navigation path to the datagrid only exists after the column is added to the Styles
			if (_bIsComboBound == false) 
			{
				_bIsComboBound = true;
				// important step here if we want to properly handle key events! the next step cannot 
				// be performed until the object is bound to the DataGrid (or an Exception must occur)
				this.DataGridTableStyle.DataGrid.Controls.Add(_cboColumn);
			}
			// this data is used when the combo box loses focus
			_iRowNum = rowNum;
			_cmSource = source;
			// synchronize the font size to the text box
			_cboColumn.Font = this.TextBox.Font;
			// we need to retrieve the current value and use this to set the combo box ahead of displaying it
			object anObj = this.GetColumnValueAtRow(source, rowNum);
			// set the combobox to the dimensions of the cell (do this each time because the user may have resized this column)
			_cboColumn.Bounds = bounds;
			// do not paint the control until we've set the correct position in the items list
			_cboColumn.BeginUpdate();
			// note: on the very first time this routine is called you MUST set the column as visible 
			// ahead of setting a position in the items collection. otherwise the combobox will not be
			// populated and the call to set the SelectedValue cannot succeed
			_cboColumn.Visible = true;
			// use the object to set the combobox. the null detection is primarily aimed at the addition of a
			// new row (where it is possible a default column-row content has not been defined)
			if (anObj.GetType() != typeof(System.DBNull)) 
			{
				_cboColumn.SelectedValue = anObj;
			} 
			else 
			{
				_cboColumn.SelectedIndex = 0;
			}
			// we've set the combobox so we can now paint the control and move focus onto it
			_cboColumn.EndUpdate();
			_cboColumn.Focus();
			// below is the default method which we definitely DONT want to call as the text box must remain dormant!
			// base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);
		}

		public void cboColumn_Leave(object sender, EventArgs e) 
		{
			// We are going to write back the ValueMember from combobox into the current column-row in the 
			// table displayed by the DataGrid control. Finally we hide the combobox. note the source and 
			// row were saved when the edit began - see Edit()
			object objValue = _cboColumn.SelectedValue;
			// we can write System.DBNull back to a database but we cannot write null (which would
			// cause an exception). if the combobox is defined as a dropdownlist we cannot see the
			// null value. However if the combobox is defined as a dropdown then editing into the
			// combobox will, by default, generate a null value. For this possibility we translate
			// null to the System.DBNull value
			if (objValue == null) 
			{ 
				objValue = DBNull.Value; 
			}
			this.SetColumnValueAtRow(_cmSource, _iRowNum, objValue);
			_cboColumn.Visible = false;
		}

		// this method is called to draw the box without a highlight (ie when the cell is in unselected state)
		protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight)
		{
			
			// there are three Paint() methods that can be overriden. I put break points in all three, but this
			// was the only one I caught. If you find odd paint behaviors please let me know and I'll investigate
			
			string strCountry = "Huh?";
			DataRow[] aRowA;

			// retrieve the value at the current column-row within the source for this column
			object anObj = this.GetColumnValueAtRow(source, rowNum);
			// use this value to access the datasource. again, we must allow that a null object
			// is returned; this typically only happens when adding a new row to the DataGrid host
			Type aType = anObj.GetType();
			if (aType != typeof(System.DBNull)) 
			{
				aRowA = ((DataTable)_objSource).Select(_strValue + " = " + anObj.ToString());
			} 
			else 
			{
				aRowA = ((DataTable)_objSource).Select();
			}
			if (aRowA.Length > 0) 
			{
				strCountry = aRowA[0][_strMember].ToString();
			}
			// all we are going to do is repaint the cell. Empiric observation indicates this code is ONLY 
			// called when the column is not in edit mode, however you could wrap conditional code around 
			// this to block an unwanted paint event calling during an edit operation
			Rectangle rect = bounds;
			// use custom background color if the property was set by the User
			if (this._backBrush == null) 
				g.FillRectangle(backBrush, rect); 
			else 
				g.FillRectangle(_backBrush, rect);
			// vertical offset to account for frame of combobox
			rect.Y += 2;
			if (this._foreBrush == null) 
				g.DrawString(strCountry, this.TextBox.Font, foreBrush, rect); 
			else
				g.DrawString(strCountry, this.TextBox.Font, _foreBrush, rect);
		}

		public System.Drawing.Color backgroundColour 
		{
			set { if (value == System.Drawing.Color.Transparent) this._backBrush = null; else this._backBrush = new SolidBrush(value);  }
		}

		public System.Drawing.Color foregroundColour 
		{
			set { if (value == System.Drawing.Color.Transparent) this._foreBrush = null; else this._foreBrush = new SolidBrush(value);  }
		}

		// below are the two other Paint() and the other Edit() override. if you re-enable the code and set a 
		// breakpoint in each, you can test to see if either method gets called!
		/*
		protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum)
		{
			base.Paint(g, bounds, source, rowNum);
		}

		protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, bool alignToRight)
		{
			base.Paint(g, bounds, source, rowNum, alignToRight);	
		}
		
		protected override void Edit(CurrencyManager source, int rowNum, Rectangle bounds, bool readOnly)
		{
			base.Edit (source, rowNum, bounds, readOnly);
		}
		*/
	}
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I am Borg. I have six computers, a Lego mindstorm with video camera and I'm currently building a Dalek.

Generally I spend so much time in front of computers I probably glow in the dark.

However I like skiing, flying aircraft and the company of old women and young wines. Hang on? Isnt' that the wrong way round? I forget.

I will answer any question about anything. I am particularly good at things I know nothing about.

Where is my gin and tonic?

Comments and Discussions