/CustomGridA/CustomDataGridreal/CS
============================
CustomColumnStyles.cs
using System;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace CustomDataGrid
{
#region Formattable TextBox Column
public class FormattableTextBoxColumn : DataGridTextBoxColumn
{
public event FormatCellEventHandler SetCellFormat;
//used to fire an event to retrieve formatting info
//and then draw the cell with this formatting info
protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight)
{
DataGridFormatCellEventArgs e = null;
bool callBaseClass = true;
//fire the formatting event
if(SetCellFormat != null)
{
int col = this.DataGridTableStyle.GridColumnStyles.IndexOf(this);
e = new DataGridFormatCellEventArgs(rowNum, col, this.GetColumnValueAtRow(source, rowNum));
SetCellFormat(this, e);
if(e.BackBrush != null)
backBrush = e.BackBrush;
//if these properties set, then must call drawstring
if(e.ForeBrush != null || e.TextFont != null)
{
if(e.ForeBrush == null)
e.ForeBrush = foreBrush;
if(e.TextFont == null)
e.TextFont = this.DataGridTableStyle.DataGrid.Font;
g.FillRectangle(backBrush, bounds);
Region saveRegion = g.Clip;
Rectangle rect = new Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height);
using(Region newRegion = new Region(rect))
{
g.Clip = newRegion;
int charWidth = (int) Math.Ceiling(g.MeasureString("c", e.TextFont, 20, StringFormat.GenericTypographic).Width);
string s = this.GetColumnValueAtRow(source, rowNum).ToString();
int maxChars = Math.Min(s.Length, (bounds.Width / charWidth));
try
{
g.DrawString(s.Substring(0, maxChars), e.TextFont, e.ForeBrush, bounds.X, bounds.Y + 2);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message.ToString());
} //empty catch
finally
{
g.Clip = saveRegion;
}
}
callBaseClass = false;
}
if(!e.UseBaseClassDrawing)
{
callBaseClass = false;
}
}
if(callBaseClass)
base.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight);
//clean up
if(e != null)
{
if(e.BackBrushDispose)
e.BackBrush.Dispose();
if(e.ForeBrushDispose)
e.ForeBrush.Dispose();
if(e.TextFontDispose)
e.TextFont.Dispose();
}
}
}
#endregion
#region Formattable Bool Column
public class FormattableBooleanColumn : DataGridBoolColumn
{
public event FormatCellEventHandler SetCellFormat;
//overridden to fire BoolChange event and Formatting event
protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight)
{
int colNum = this.DataGridTableStyle.GridColumnStyles.IndexOf(this);
//used to handle the boolchanging
ManageBoolValueChanging(rowNum, colNum);
//fire formatting event
DataGridFormatCellEventArgs e = null;
bool callBaseClass = true;
if(SetCellFormat != null)
{
e = new DataGridFormatCellEventArgs(rowNum, colNum, this.GetColumnValueAtRow(source, rowNum));
SetCellFormat(this, e);
if(e.BackBrush != null)
backBrush = e.BackBrush;
callBaseClass = e.UseBaseClassDrawing;
}
if(callBaseClass)
base.Paint(g, bounds, source, rowNum, backBrush, new SolidBrush(Color.Red), alignToRight);
//clean up
if(e != null)
{
if(e.BackBrushDispose)
e.BackBrush.Dispose();
if(e.ForeBrushDispose)
e.ForeBrush.Dispose();
if(e.TextFontDispose)
e.TextFont.Dispose();
}
}
//changed event
public event BoolValueChangedEventHandler BoolValueChanged;
bool saveValue = false;
int saveRow = -1;
bool lockValue = false;
bool beingEdited = false;
public const int VK_SPACE = 32 ;// 0x20
//needed to get the space bar changing of the bool value
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern short GetKeyState(int nVirtKey);
//set variables to start tracking bool changes
protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible)
{
lockValue = true;
beingEdited = true;
saveRow = rowNum;
saveValue = (bool) base.GetColumnValueAtRow(source, rowNum);
base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);
}
//turn off tracking bool changes
protected override bool Commit(System.Windows.Forms.CurrencyManager dataSource, int rowNum)
{
lockValue = true;
beingEdited = false;
return base.Commit(dataSource, rowNum);
}
//fire the bool change event if the value changes
private void ManageBoolValueChanging(int rowNum, int colNum)
{
Point mousePos = this.DataGridTableStyle.DataGrid.PointToClient(Control.MousePosition);
DataGrid dg = this.DataGridTableStyle.DataGrid;
bool isClickInCell = ((Control.MouseButtons == MouseButtons.Left) &&
dg.GetCellBounds(dg.CurrentCell).Contains(mousePos) );
bool changing = dg.Focused && ( isClickInCell
|| GetKeyState(VK_SPACE) < 0 ); // or spacebar
if(!lockValue && beingEdited && changing && saveRow == rowNum)
{
saveValue = !saveValue;
lockValue = false;
//fire the event
if(BoolValueChanged != null)
{
BoolValueChangedEventArgs e = new BoolValueChangedEventArgs(rowNum, colNum, saveValue);
BoolValueChanged(this, e);
}
}
if(saveRow == rowNum)
lockValue = false;
}
}
#endregion
#region CellFormatting Event
public delegate void FormatCellEventHandler(object sender, DataGridFormatCellEventArgs e);
public class DataGridFormatCellEventArgs : EventArgs
{
private int colNum;
private int rowNum;
private Font fontVal;
private Brush backBrushVal;
private Brush foreBrushVal;
private bool fontDispose;
private bool backBrushDispose;
private bool foreBrushDispose;
private bool useBaseClassDrawingVal;
private object currentCellValue;
public DataGridFormatCellEventArgs(int row, int col, object cellValue)
{
rowNum = row;
colNum = col;
fontVal = null;
backBrushVal = null;
foreBrushVal = null;
fontDispose = false;
backBrushDispose = false;
foreBrushDispose = false;
useBaseClassDrawingVal = true;
currentCellValue = cellValue;
}
//column being painted
public int Column
{
get{ return colNum;}
set{ colNum = value;}
}
//row being painted
public int Row
{
get{ return rowNum;}
set{ rowNum = value;}
}
//font used for drawing the text
public Font TextFont
{
get{ return fontVal;}
set{ fontVal = value;}
}
//background brush
public Brush BackBrush
{
get{ return backBrushVal;}
set{ backBrushVal = value;}
}
//foreground brush
public Brush ForeBrush
{
get{ return foreBrushVal;}
set{ foreBrushVal = value;}
}
//set true if you want the Paint method to call Dispose on the font
public bool TextFontDispose
{
get{ return fontDispose;}
set{ fontDispose = value;}
}
//set true if you want the Paint method to call Dispose on the brush
public bool BackBrushDispose
{
get{ return backBrushDispose;}
set{ backBrushDispose = value;}
}
//set true if you want the Paint method to call Dispose on the brush
public bool ForeBrushDispose
{
get{ return foreBrushDispose;}
set{ foreBrushDispose = value;}
}
//set true if you want the Paint method to call base class
public bool UseBaseClassDrawing
{
get{ return useBaseClassDrawingVal;}
set{ useBaseClassDrawingVal = value;}
}
//contains the current cell value
public object CurrentCellValue
{
get{ return currentCellValue;}
}
}
#endregion
#region BoolValueChanging Event
public delegate void BoolValueChangedEventHandler(object sender, BoolValueChangedEventArgs e);
public class BoolValueChangedEventArgs : EventArgs
{
private int columnVal;
private int rowVal;
private bool boolVal;
public BoolValueChangedEventArgs(int row, int col, bool val)
{
rowVal = row;
columnVal = col;
boolVal = val;
}
//column to be painted
public int Column
{
get{ return columnVal;}
set{ columnVal = value;}
}
//row to be painted
public int Row
{
get{ return rowVal;}
set{ rowVal = value;}
}
//current value to be painted
public bool BoolValue
{
get{return boolVal;}
}
}
#endregion
}
===============
Form1.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
namespace CustomDataGrid
{
public class Form1 : System.Windows.Forms.Form
{
#region Fields
//created by designer
private System.Windows.Forms.DataGrid dataGrid1;
private CustomDataGrid.DataSet1 dataSet11;
private System.ComponentModel.Container components = null;
// Used to refer to index specific columns in our grid.
// Values set in CreateTheTable method.
private int unitsInStockCol;
private int unitsOnOrderCol;
private int reorderLevelCol;
private int checkCol;
private int nameCol;
// Cached GDI+ objects created in Form1_Load.
// Used in the event handlers for the custom column styles events.
private Brush disabledBackBrush;
private Brush disabledTextBrush;
private Brush alertBackBrush;
private Font alertFont;
private Brush alertTextBrush;
private Font currentRowFont;
private Brush currentRowBackBrush;
// Grid tootips-fields used by the grid's mousemove events to manage
// row specific tips, initialized in Form_Load, used in dataGrid1_MouseMove.
private int hitRow;
private System.Windows.Forms.ToolTip toolTip1;
#endregion
#region Wizard Generated Code
public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
//dispose of our cached graphics objects
if(disabledBackBrush != null)
{
disabledBackBrush.Dispose();
disabledTextBrush.Dispose();
alertBackBrush.Dispose();
alertFont.Dispose();
alertTextBrush.Dispose();
currentRowFont.Dispose();
currentRowBackBrush.Dispose();
}
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dataGrid1 = new System.Windows.Forms.DataGrid();
this.dataSet11 = new CustomDataGrid.DataSet1();
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dataSet11)).BeginInit();
this.SuspendLayout();
//
// dataGrid1
//
this.dataGrid1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right);
this.dataGrid1.CaptionText = "ReOrder Grid";
this.dataGrid1.DataMember = "";
this.dataGrid1.DataSource = this.dataSet11.Products;
this.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText;
this.dataGrid1.Location = new System.Drawing.Point(24, 24);
this.dataGrid1.Name = "dataGrid1";
this.dataGrid1.Size = new System.Drawing.Size(440, 216);
this.dataGrid1.TabIndex = 0;
this.dataGrid1.Click += new System.EventHandler(this.dataGrid1_Click);
this.dataGrid1.CurrentCellChanged += new System.EventHandler(this.dataGrid1_CurrentCellChanged);
//
// dataSet11
//
this.dataSet11.DataSetName = "DataSet1";
this.dataSet11.Locale = new System.Globalization.CultureInfo("en-US");
this.dataSet11.Namespace = "http://www.tempuri.org/DataSet1.xsd";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(488, 261);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.dataGrid1});
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dataSet11)).EndInit();
this.ResumeLayout(false);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
#endregion
#region Initialization Code
//Used to:
// Load the table.
// Set a tablestyle into the grid.
// Assign the grid's datasource.
// Create & cache some graphics objects.
private void Form1_Load(object sender, System.EventArgs e)
{
//read the data
this.dataSet11.ReadXml(GetPathTo("ProductsTable.XML"));
//set the data source
DataTable dt = this.dataSet11.Tables["Products"];
this.dataGrid1.DataSource = dt;
//add the tablestyle to the datagrid
this.dataGrid1.TableStyles.Add(CreateTheTable());
//create and cash some GDI+ objects that we are continually using
//in our cell formatting.
this.disabledBackBrush = new SolidBrush(SystemColors.InactiveCaptionText);
this.disabledTextBrush = new SolidBrush(SystemColors.GrayText);
this.alertBackBrush = new SolidBrush(SystemColors.HotTrack);
this.alertFont = new Font(this.dataGrid1.Font.Name, this.dataGrid1.Font.Size, FontStyle.Bold);
this.alertTextBrush = new SolidBrush(Color.White);
this.currentRowFont = new Font(this.dataGrid1.Font.Name, this.dataGrid1.Font.Size, FontStyle.Bold);
this.currentRowBackBrush = new SolidBrush(Color.FromArgb(255, 200, 200));
//set up the grid tooltips
this.hitRow = -1;
this.toolTip1 = new ToolTip();
this.toolTip1.InitialDelay = 300;
this.dataGrid1.MouseMove += new MouseEventHandler(dataGrid1_MouseMove);
}
private DataGridTableStyle CreateTheTable()
{
//create an empty DataGridTableStyle & set mapping name to table
DataGridTableStyle tableStyle = new DataGridTableStyle();
tableStyle.MappingName = "Products";
//now create custom DataGridColumnSyle for each col we want to
//appear in the grid and in the order that we want to see them
//Discontinued
FormattableBooleanColumn discontinuedCol = new FormattableBooleanColumn();
discontinuedCol.MappingName = "Discontinued";
discontinuedCol.HeaderText = "";
discontinuedCol.Width = 30;
discontinuedCol.AllowNull = false; // turn off tristate
this.checkCol = 0;
discontinuedCol.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
discontinuedCol.BoolValueChanged += new BoolValueChangedEventHandler(BoolValueChanged);
tableStyle.GridColumnStyles.Add(discontinuedCol);
//ProductID
FormattableTextBoxColumn column = new FormattableTextBoxColumn();
column.MappingName = "ProductID";
column.HeaderText = "ID";
column.Width = 30;
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
//ProductName
column = new FormattableTextBoxColumn();
column.MappingName = "ProductName";
column.HeaderText = "Name";
column.Width = 140;
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
this.nameCol = 2;
// lets hide the next 2 columns by not adding a
// GridColumnStyle to the tableStyle
// //SupplierID
// column = new FormattableTextBoxColumn();
// column.MappingName = "SupplierID";
// column.HeaderText = "SupplierID";
// column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
// tableStyle.GridColumnStyles.Add(column);
// //CategoryID
// column = new FormattableTextBoxColumn();
// column.MappingName = "CategoryID";
// column.HeaderText = "CategoryID";
// column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
// tableStyle.GridColumnStyles.Add(column);
//QuantityPerUnit
column = new FormattableTextBoxColumn();
column.MappingName = "QuantityPerUnit";
column.HeaderText = "QuantityPerUnit";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
//UnitPrice
column = new FormattableTextBoxColumn();
column.MappingName = "UnitPrice";
column.HeaderText = "UnitPrice";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
//UnitsInStock
column = new FormattableTextBoxColumn();
column.MappingName = "UnitsInStock";
column.HeaderText = "UnitsInStock";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
this.unitsInStockCol = 5;
//UnitsOnOrder
column = new FormattableTextBoxColumn();
column.MappingName = "UnitsOnOrder";
column.HeaderText = "UnitsOnOrder";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
this.unitsOnOrderCol = 6;
//ReorderLevel
column = new FormattableTextBoxColumn();
column.MappingName = "ReorderLevel";
column.HeaderText = "ReorderLevel";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
this.reorderLevelCol = 7;
return tableStyle;
}
//look back up in the project path for file...
private string GetPathTo(string name)
{
for(int i = 0; i < 3; ++i)
{
if(File.Exists(name))
break;
name = @"../" + name;
}
return name;
}
#endregion
#region Custom ColumnStyle Events
// Provides the format for the given cell.
private void SetCellFormat(object sender, DataGridFormatCellEventArgs e)
{
//conditionally set properties in e depending upon e.Row and e.Col
bool discontinued = (bool) ((e.Column != 0) ? this.dataGrid1[e.Row, 0] : e.CurrentCellValue);
//check is discontinued
if(e.Column > 0 && (bool)(this.dataGrid1[e.Row, 0]))//discontinued)
{
e.BackBrush = this.disabledBackBrush;
e.ForeBrush = this.disabledTextBrush;
}
else if(e.Column > 0 && e.Row == this.dataGrid1.CurrentRowIndex)//discontinued)
{
e.BackBrush = this.currentRowBackBrush;
e.TextFont = this.currentRowFont;
}
else if(NeedToReorder(e.Row))
{
e.TextFont = this.alertFont;
e.ForeBrush = this.alertTextBrush;
e.BackBrush = this.alertBackBrush;
}
}
// Handles the changing of a boolvalue:
// Forces the value committed and then redraws the row.
// The value is committed so we can test it in our drawing
// code by just accessing the DataGridValue.
private void BoolValueChanged(object sender, BoolValueChangedEventArgs e)
{
this.dataGrid1.EndEdit(this.dataGrid1.TableStyles[0].GridColumnStyles["Discontinued"],e.Row, false);
RefreshRow(e.Row);
this.dataGrid1.BeginEdit(this.dataGrid1.TableStyles[0].GridColumnStyles["Discontinued"],e.Row);
}
#endregion
#region ToolTip Implementation
// Checks mouse position to see if it is over a discontinued row,
// or reorder row. If so, set the tiptext and reset the tooltip.
private void dataGrid1_MouseMove(object sender, MouseEventArgs e)
{
DataGrid.HitTestInfo hti = this.dataGrid1.HitTest(new Point(e.X, e.Y));
BindingManagerBase bmb = this.BindingContext[this.dataGrid1.DataSource, this.dataGrid1.DataMember];
if(hti.Row < bmb.Count && hti.Type == DataGrid.HitTestType.Cell
&& hti.Row != hitRow )
{
if(this.toolTip1 != null && this.toolTip1.Active)
this.toolTip1.Active = false; //turn it off
string tipText = "";
if((bool)this.dataGrid1[hti.Row, checkCol])
tipText = this.dataGrid1[hti.Row, nameCol].ToString() + " discontinued";
else if (this.NeedToReorder(hti.Row))
{
tipText = this.dataGrid1[hti.Row, nameCol].ToString() + " order NOW";
}
if(tipText != "")
{
//new hit row
hitRow = hti.Row;
this.toolTip1.SetToolTip(this.dataGrid1, tipText);
this.toolTip1.Active = true; //make it active so it can show itself
}
else
hitRow = -1;
}
}
#endregion
#region OneClickBoolColumn Implementation
// If there is a click on a dicontinued row, want the checkbox
// to be the active currentcell.
// afterCurrentCellChanged is set in dataGrid1_CurrentCellChanged.
private void dataGrid1_Click(object sender, System.EventArgs e)
{
Point pt = this.dataGrid1.PointToClient(Control.MousePosition);
DataGrid.HitTestInfo hti = this.dataGrid1.HitTest(pt);
BindingManagerBase bmb = this.BindingContext[this.dataGrid1.DataSource, this.dataGrid1.DataMember];
if(afterCurrentCellChanged && hti.Row < bmb.Count
&& hti.Type == DataGrid.HitTestType.Cell
&& hti.Column == checkCol )
{
this.dataGrid1[hti.Row, checkCol] = !(bool)this.dataGrid1[hti.Row, checkCol];
RefreshRow(hti.Row);
}
afterCurrentCellChanged = false;
}
#endregion
#region Helper Methods
// Forces a repaint of given row.
private void RefreshRow(int row)
{
Rectangle rect = this.dataGrid1.GetCellBounds(row, 0);
rect = new Rectangle(rect.Right, rect.Top, this.dataGrid1.Width, rect.Height);
this.dataGrid1.Invalidate(rect);
}
// Given a row, computes whether product needs tobe reordered.
private bool NeedToReorder(int row)
{
int unitsInStock = (Int16) this.dataGrid1[row, this.unitsInStockCol];
int unitsOnOrder = (Int16) this.dataGrid1[row, this.unitsOnOrderCol];
int reorderLevel = (Int16) this.dataGrid1[row, this.reorderLevelCol];
return (reorderLevel > (unitsInStock + unitsOnOrder));
}
//DataGrid.CurrentCellChanged used to:
// Force current cell to bool column if click on discontinued row.
// Set afterCurrentCellFlag which is part of the OneClickBoolColumn.
private bool afterCurrentCellChanged = false;
private void dataGrid1_CurrentCellChanged(object sender, System.EventArgs e)
{
//if click on a discontinued row, then set currrentcell to checkbox
if((bool)this.dataGrid1[this.dataGrid1.CurrentRowIndex, checkCol])
this.dataGrid1.CurrentCell = new DataGridCell(this.dataGrid1.CurrentRowIndex, checkCol);
afterCurrentCellChanged = true;
}
#endregion
}
}
============================
CustomColumnStyles.cs
using System;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace CustomDataGrid
{
#region Formattable TextBox Column
public class FormattableTextBoxColumn : DataGridTextBoxColumn
{
public event FormatCellEventHandler SetCellFormat;
//used to fire an event to retrieve formatting info
//and then draw the cell with this formatting info
protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight)
{
DataGridFormatCellEventArgs e = null;
bool callBaseClass = true;
//fire the formatting event
if(SetCellFormat != null)
{
int col = this.DataGridTableStyle.GridColumnStyles.IndexOf(this);
e = new DataGridFormatCellEventArgs(rowNum, col, this.GetColumnValueAtRow(source, rowNum));
SetCellFormat(this, e);
if(e.BackBrush != null)
backBrush = e.BackBrush;
//if these properties set, then must call drawstring
if(e.ForeBrush != null || e.TextFont != null)
{
if(e.ForeBrush == null)
e.ForeBrush = foreBrush;
if(e.TextFont == null)
e.TextFont = this.DataGridTableStyle.DataGrid.Font;
g.FillRectangle(backBrush, bounds);
Region saveRegion = g.Clip;
Rectangle rect = new Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height);
using(Region newRegion = new Region(rect))
{
g.Clip = newRegion;
int charWidth = (int) Math.Ceiling(g.MeasureString("c", e.TextFont, 20, StringFormat.GenericTypographic).Width);
string s = this.GetColumnValueAtRow(source, rowNum).ToString();
int maxChars = Math.Min(s.Length, (bounds.Width / charWidth));
try
{
g.DrawString(s.Substring(0, maxChars), e.TextFont, e.ForeBrush, bounds.X, bounds.Y + 2);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message.ToString());
} //empty catch
finally
{
g.Clip = saveRegion;
}
}
callBaseClass = false;
}
if(!e.UseBaseClassDrawing)
{
callBaseClass = false;
}
}
if(callBaseClass)
base.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight);
//clean up
if(e != null)
{
if(e.BackBrushDispose)
e.BackBrush.Dispose();
if(e.ForeBrushDispose)
e.ForeBrush.Dispose();
if(e.TextFontDispose)
e.TextFont.Dispose();
}
}
}
#endregion
#region Formattable Bool Column
public class FormattableBooleanColumn : DataGridBoolColumn
{
public event FormatCellEventHandler SetCellFormat;
//overridden to fire BoolChange event and Formatting event
protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight)
{
int colNum = this.DataGridTableStyle.GridColumnStyles.IndexOf(this);
//used to handle the boolchanging
ManageBoolValueChanging(rowNum, colNum);
//fire formatting event
DataGridFormatCellEventArgs e = null;
bool callBaseClass = true;
if(SetCellFormat != null)
{
e = new DataGridFormatCellEventArgs(rowNum, colNum, this.GetColumnValueAtRow(source, rowNum));
SetCellFormat(this, e);
if(e.BackBrush != null)
backBrush = e.BackBrush;
callBaseClass = e.UseBaseClassDrawing;
}
if(callBaseClass)
base.Paint(g, bounds, source, rowNum, backBrush, new SolidBrush(Color.Red), alignToRight);
//clean up
if(e != null)
{
if(e.BackBrushDispose)
e.BackBrush.Dispose();
if(e.ForeBrushDispose)
e.ForeBrush.Dispose();
if(e.TextFontDispose)
e.TextFont.Dispose();
}
}
//changed event
public event BoolValueChangedEventHandler BoolValueChanged;
bool saveValue = false;
int saveRow = -1;
bool lockValue = false;
bool beingEdited = false;
public const int VK_SPACE = 32 ;// 0x20
//needed to get the space bar changing of the bool value
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern short GetKeyState(int nVirtKey);
//set variables to start tracking bool changes
protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible)
{
lockValue = true;
beingEdited = true;
saveRow = rowNum;
saveValue = (bool) base.GetColumnValueAtRow(source, rowNum);
base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);
}
//turn off tracking bool changes
protected override bool Commit(System.Windows.Forms.CurrencyManager dataSource, int rowNum)
{
lockValue = true;
beingEdited = false;
return base.Commit(dataSource, rowNum);
}
//fire the bool change event if the value changes
private void ManageBoolValueChanging(int rowNum, int colNum)
{
Point mousePos = this.DataGridTableStyle.DataGrid.PointToClient(Control.MousePosition);
DataGrid dg = this.DataGridTableStyle.DataGrid;
bool isClickInCell = ((Control.MouseButtons == MouseButtons.Left) &&
dg.GetCellBounds(dg.CurrentCell).Contains(mousePos) );
bool changing = dg.Focused && ( isClickInCell
|| GetKeyState(VK_SPACE) < 0 ); // or spacebar
if(!lockValue && beingEdited && changing && saveRow == rowNum)
{
saveValue = !saveValue;
lockValue = false;
//fire the event
if(BoolValueChanged != null)
{
BoolValueChangedEventArgs e = new BoolValueChangedEventArgs(rowNum, colNum, saveValue);
BoolValueChanged(this, e);
}
}
if(saveRow == rowNum)
lockValue = false;
}
}
#endregion
#region CellFormatting Event
public delegate void FormatCellEventHandler(object sender, DataGridFormatCellEventArgs e);
public class DataGridFormatCellEventArgs : EventArgs
{
private int colNum;
private int rowNum;
private Font fontVal;
private Brush backBrushVal;
private Brush foreBrushVal;
private bool fontDispose;
private bool backBrushDispose;
private bool foreBrushDispose;
private bool useBaseClassDrawingVal;
private object currentCellValue;
public DataGridFormatCellEventArgs(int row, int col, object cellValue)
{
rowNum = row;
colNum = col;
fontVal = null;
backBrushVal = null;
foreBrushVal = null;
fontDispose = false;
backBrushDispose = false;
foreBrushDispose = false;
useBaseClassDrawingVal = true;
currentCellValue = cellValue;
}
//column being painted
public int Column
{
get{ return colNum;}
set{ colNum = value;}
}
//row being painted
public int Row
{
get{ return rowNum;}
set{ rowNum = value;}
}
//font used for drawing the text
public Font TextFont
{
get{ return fontVal;}
set{ fontVal = value;}
}
//background brush
public Brush BackBrush
{
get{ return backBrushVal;}
set{ backBrushVal = value;}
}
//foreground brush
public Brush ForeBrush
{
get{ return foreBrushVal;}
set{ foreBrushVal = value;}
}
//set true if you want the Paint method to call Dispose on the font
public bool TextFontDispose
{
get{ return fontDispose;}
set{ fontDispose = value;}
}
//set true if you want the Paint method to call Dispose on the brush
public bool BackBrushDispose
{
get{ return backBrushDispose;}
set{ backBrushDispose = value;}
}
//set true if you want the Paint method to call Dispose on the brush
public bool ForeBrushDispose
{
get{ return foreBrushDispose;}
set{ foreBrushDispose = value;}
}
//set true if you want the Paint method to call base class
public bool UseBaseClassDrawing
{
get{ return useBaseClassDrawingVal;}
set{ useBaseClassDrawingVal = value;}
}
//contains the current cell value
public object CurrentCellValue
{
get{ return currentCellValue;}
}
}
#endregion
#region BoolValueChanging Event
public delegate void BoolValueChangedEventHandler(object sender, BoolValueChangedEventArgs e);
public class BoolValueChangedEventArgs : EventArgs
{
private int columnVal;
private int rowVal;
private bool boolVal;
public BoolValueChangedEventArgs(int row, int col, bool val)
{
rowVal = row;
columnVal = col;
boolVal = val;
}
//column to be painted
public int Column
{
get{ return columnVal;}
set{ columnVal = value;}
}
//row to be painted
public int Row
{
get{ return rowVal;}
set{ rowVal = value;}
}
//current value to be painted
public bool BoolValue
{
get{return boolVal;}
}
}
#endregion
}
===============
Form1.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
namespace CustomDataGrid
{
public class Form1 : System.Windows.Forms.Form
{
#region Fields
//created by designer
private System.Windows.Forms.DataGrid dataGrid1;
private CustomDataGrid.DataSet1 dataSet11;
private System.ComponentModel.Container components = null;
// Used to refer to index specific columns in our grid.
// Values set in CreateTheTable method.
private int unitsInStockCol;
private int unitsOnOrderCol;
private int reorderLevelCol;
private int checkCol;
private int nameCol;
// Cached GDI+ objects created in Form1_Load.
// Used in the event handlers for the custom column styles events.
private Brush disabledBackBrush;
private Brush disabledTextBrush;
private Brush alertBackBrush;
private Font alertFont;
private Brush alertTextBrush;
private Font currentRowFont;
private Brush currentRowBackBrush;
// Grid tootips-fields used by the grid's mousemove events to manage
// row specific tips, initialized in Form_Load, used in dataGrid1_MouseMove.
private int hitRow;
private System.Windows.Forms.ToolTip toolTip1;
#endregion
#region Wizard Generated Code
public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
//dispose of our cached graphics objects
if(disabledBackBrush != null)
{
disabledBackBrush.Dispose();
disabledTextBrush.Dispose();
alertBackBrush.Dispose();
alertFont.Dispose();
alertTextBrush.Dispose();
currentRowFont.Dispose();
currentRowBackBrush.Dispose();
}
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dataGrid1 = new System.Windows.Forms.DataGrid();
this.dataSet11 = new CustomDataGrid.DataSet1();
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dataSet11)).BeginInit();
this.SuspendLayout();
//
// dataGrid1
//
this.dataGrid1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right);
this.dataGrid1.CaptionText = "ReOrder Grid";
this.dataGrid1.DataMember = "";
this.dataGrid1.DataSource = this.dataSet11.Products;
this.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText;
this.dataGrid1.Location = new System.Drawing.Point(24, 24);
this.dataGrid1.Name = "dataGrid1";
this.dataGrid1.Size = new System.Drawing.Size(440, 216);
this.dataGrid1.TabIndex = 0;
this.dataGrid1.Click += new System.EventHandler(this.dataGrid1_Click);
this.dataGrid1.CurrentCellChanged += new System.EventHandler(this.dataGrid1_CurrentCellChanged);
//
// dataSet11
//
this.dataSet11.DataSetName = "DataSet1";
this.dataSet11.Locale = new System.Globalization.CultureInfo("en-US");
this.dataSet11.Namespace = "http://www.tempuri.org/DataSet1.xsd";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(488, 261);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.dataGrid1});
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dataSet11)).EndInit();
this.ResumeLayout(false);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
#endregion
#region Initialization Code
//Used to:
// Load the table.
// Set a tablestyle into the grid.
// Assign the grid's datasource.
// Create & cache some graphics objects.
private void Form1_Load(object sender, System.EventArgs e)
{
//read the data
this.dataSet11.ReadXml(GetPathTo("ProductsTable.XML"));
//set the data source
DataTable dt = this.dataSet11.Tables["Products"];
this.dataGrid1.DataSource = dt;
//add the tablestyle to the datagrid
this.dataGrid1.TableStyles.Add(CreateTheTable());
//create and cash some GDI+ objects that we are continually using
//in our cell formatting.
this.disabledBackBrush = new SolidBrush(SystemColors.InactiveCaptionText);
this.disabledTextBrush = new SolidBrush(SystemColors.GrayText);
this.alertBackBrush = new SolidBrush(SystemColors.HotTrack);
this.alertFont = new Font(this.dataGrid1.Font.Name, this.dataGrid1.Font.Size, FontStyle.Bold);
this.alertTextBrush = new SolidBrush(Color.White);
this.currentRowFont = new Font(this.dataGrid1.Font.Name, this.dataGrid1.Font.Size, FontStyle.Bold);
this.currentRowBackBrush = new SolidBrush(Color.FromArgb(255, 200, 200));
//set up the grid tooltips
this.hitRow = -1;
this.toolTip1 = new ToolTip();
this.toolTip1.InitialDelay = 300;
this.dataGrid1.MouseMove += new MouseEventHandler(dataGrid1_MouseMove);
}
private DataGridTableStyle CreateTheTable()
{
//create an empty DataGridTableStyle & set mapping name to table
DataGridTableStyle tableStyle = new DataGridTableStyle();
tableStyle.MappingName = "Products";
//now create custom DataGridColumnSyle for each col we want to
//appear in the grid and in the order that we want to see them
//Discontinued
FormattableBooleanColumn discontinuedCol = new FormattableBooleanColumn();
discontinuedCol.MappingName = "Discontinued";
discontinuedCol.HeaderText = "";
discontinuedCol.Width = 30;
discontinuedCol.AllowNull = false; // turn off tristate
this.checkCol = 0;
discontinuedCol.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
discontinuedCol.BoolValueChanged += new BoolValueChangedEventHandler(BoolValueChanged);
tableStyle.GridColumnStyles.Add(discontinuedCol);
//ProductID
FormattableTextBoxColumn column = new FormattableTextBoxColumn();
column.MappingName = "ProductID";
column.HeaderText = "ID";
column.Width = 30;
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
//ProductName
column = new FormattableTextBoxColumn();
column.MappingName = "ProductName";
column.HeaderText = "Name";
column.Width = 140;
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
this.nameCol = 2;
// lets hide the next 2 columns by not adding a
// GridColumnStyle to the tableStyle
// //SupplierID
// column = new FormattableTextBoxColumn();
// column.MappingName = "SupplierID";
// column.HeaderText = "SupplierID";
// column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
// tableStyle.GridColumnStyles.Add(column);
// //CategoryID
// column = new FormattableTextBoxColumn();
// column.MappingName = "CategoryID";
// column.HeaderText = "CategoryID";
// column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
// tableStyle.GridColumnStyles.Add(column);
//QuantityPerUnit
column = new FormattableTextBoxColumn();
column.MappingName = "QuantityPerUnit";
column.HeaderText = "QuantityPerUnit";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
//UnitPrice
column = new FormattableTextBoxColumn();
column.MappingName = "UnitPrice";
column.HeaderText = "UnitPrice";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
//UnitsInStock
column = new FormattableTextBoxColumn();
column.MappingName = "UnitsInStock";
column.HeaderText = "UnitsInStock";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
this.unitsInStockCol = 5;
//UnitsOnOrder
column = new FormattableTextBoxColumn();
column.MappingName = "UnitsOnOrder";
column.HeaderText = "UnitsOnOrder";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
this.unitsOnOrderCol = 6;
//ReorderLevel
column = new FormattableTextBoxColumn();
column.MappingName = "ReorderLevel";
column.HeaderText = "ReorderLevel";
column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
tableStyle.GridColumnStyles.Add(column);
this.reorderLevelCol = 7;
return tableStyle;
}
//look back up in the project path for file...
private string GetPathTo(string name)
{
for(int i = 0; i < 3; ++i)
{
if(File.Exists(name))
break;
name = @"../" + name;
}
return name;
}
#endregion
#region Custom ColumnStyle Events
// Provides the format for the given cell.
private void SetCellFormat(object sender, DataGridFormatCellEventArgs e)
{
//conditionally set properties in e depending upon e.Row and e.Col
bool discontinued = (bool) ((e.Column != 0) ? this.dataGrid1[e.Row, 0] : e.CurrentCellValue);
//check is discontinued
if(e.Column > 0 && (bool)(this.dataGrid1[e.Row, 0]))//discontinued)
{
e.BackBrush = this.disabledBackBrush;
e.ForeBrush = this.disabledTextBrush;
}
else if(e.Column > 0 && e.Row == this.dataGrid1.CurrentRowIndex)//discontinued)
{
e.BackBrush = this.currentRowBackBrush;
e.TextFont = this.currentRowFont;
}
else if(NeedToReorder(e.Row))
{
e.TextFont = this.alertFont;
e.ForeBrush = this.alertTextBrush;
e.BackBrush = this.alertBackBrush;
}
}
// Handles the changing of a boolvalue:
// Forces the value committed and then redraws the row.
// The value is committed so we can test it in our drawing
// code by just accessing the DataGridValue.
private void BoolValueChanged(object sender, BoolValueChangedEventArgs e)
{
this.dataGrid1.EndEdit(this.dataGrid1.TableStyles[0].GridColumnStyles["Discontinued"],e.Row, false);
RefreshRow(e.Row);
this.dataGrid1.BeginEdit(this.dataGrid1.TableStyles[0].GridColumnStyles["Discontinued"],e.Row);
}
#endregion
#region ToolTip Implementation
// Checks mouse position to see if it is over a discontinued row,
// or reorder row. If so, set the tiptext and reset the tooltip.
private void dataGrid1_MouseMove(object sender, MouseEventArgs e)
{
DataGrid.HitTestInfo hti = this.dataGrid1.HitTest(new Point(e.X, e.Y));
BindingManagerBase bmb = this.BindingContext[this.dataGrid1.DataSource, this.dataGrid1.DataMember];
if(hti.Row < bmb.Count && hti.Type == DataGrid.HitTestType.Cell
&& hti.Row != hitRow )
{
if(this.toolTip1 != null && this.toolTip1.Active)
this.toolTip1.Active = false; //turn it off
string tipText = "";
if((bool)this.dataGrid1[hti.Row, checkCol])
tipText = this.dataGrid1[hti.Row, nameCol].ToString() + " discontinued";
else if (this.NeedToReorder(hti.Row))
{
tipText = this.dataGrid1[hti.Row, nameCol].ToString() + " order NOW";
}
if(tipText != "")
{
//new hit row
hitRow = hti.Row;
this.toolTip1.SetToolTip(this.dataGrid1, tipText);
this.toolTip1.Active = true; //make it active so it can show itself
}
else
hitRow = -1;
}
}
#endregion
#region OneClickBoolColumn Implementation
// If there is a click on a dicontinued row, want the checkbox
// to be the active currentcell.
// afterCurrentCellChanged is set in dataGrid1_CurrentCellChanged.
private void dataGrid1_Click(object sender, System.EventArgs e)
{
Point pt = this.dataGrid1.PointToClient(Control.MousePosition);
DataGrid.HitTestInfo hti = this.dataGrid1.HitTest(pt);
BindingManagerBase bmb = this.BindingContext[this.dataGrid1.DataSource, this.dataGrid1.DataMember];
if(afterCurrentCellChanged && hti.Row < bmb.Count
&& hti.Type == DataGrid.HitTestType.Cell
&& hti.Column == checkCol )
{
this.dataGrid1[hti.Row, checkCol] = !(bool)this.dataGrid1[hti.Row, checkCol];
RefreshRow(hti.Row);
}
afterCurrentCellChanged = false;
}
#endregion
#region Helper Methods
// Forces a repaint of given row.
private void RefreshRow(int row)
{
Rectangle rect = this.dataGrid1.GetCellBounds(row, 0);
rect = new Rectangle(rect.Right, rect.Top, this.dataGrid1.Width, rect.Height);
this.dataGrid1.Invalidate(rect);
}
// Given a row, computes whether product needs tobe reordered.
private bool NeedToReorder(int row)
{
int unitsInStock = (Int16) this.dataGrid1[row, this.unitsInStockCol];
int unitsOnOrder = (Int16) this.dataGrid1[row, this.unitsOnOrderCol];
int reorderLevel = (Int16) this.dataGrid1[row, this.reorderLevelCol];
return (reorderLevel > (unitsInStock + unitsOnOrder));
}
//DataGrid.CurrentCellChanged used to:
// Force current cell to bool column if click on discontinued row.
// Set afterCurrentCellFlag which is part of the OneClickBoolColumn.
private bool afterCurrentCellChanged = false;
private void dataGrid1_CurrentCellChanged(object sender, System.EventArgs e)
{
//if click on a discontinued row, then set currrentcell to checkbox
if((bool)this.dataGrid1[this.dataGrid1.CurrentRowIndex, checkCol])
this.dataGrid1.CurrentCell = new DataGridCell(this.dataGrid1.CurrentRowIndex, checkCol);
afterCurrentCellChanged = true;
}
#endregion
}
}