SourceGrid 2_0

SourceGrid 2.0

Sample Image - maximum width is 600 pixels

Introduction

SourceGrid is a Windows Forms control written entirely in C#. My goal was to create a simple but flexible grid to use whenever it is necessary to visualize or to change a series of data in a table format.
There are a lot of controls of this type available, but often are expensive, difficult to customize or not compatible with .NET.
The Microsoft DataGrid for me is too DataSet orientated and therefore is often complicated to use in the cases in which the source data isn't a DataSet, and often it is not customizable enough.

I want to thank Chirs Beckett, Anthony Berglas, Wayne Tanner, Ernesto Perales, Vadim Katsman, Jeffery Bell, Gmonkey, cmwalolo, Kenedy, zeromus, Darko Damjanovic, John Pierre, Achim Schäfer, Michael Look, Elmü (http://kickme.to/elmue) and a lot of other people who helped me with code, bugs report and with new ideas and suggestions.
A special thank to NDoc that I have used to create CHM help.

This control is compiled with the Microsoft Framework. NET 1.1 and reference the assembly SourceLibrary.dll 1.2.3.1, a small library with common functionalities. I introduced this dll in the ZIP file, but is possible to download the entire code and the latest binary from the site http://www.devage.com/. To use SourceGrid is necessary to have Visual Studio .NET 2003 or a compatible development environment.

The latest version of this control is downloadable from site http://www.devage.com/. If you have problems, ideas or questions write me at webmaster@devage.com .

In this article I want to supply an overview of the utilization and functionalities of the SourceGrid control. For details on the classes, properties or methods you can consult the documentation in CHM format or the example project in the ZIP file.

Using SourceGrid

Projects and configurations

In the attached file there are 4 projects:

  • SourceGrid (SourceGrid2.dll) - Main project for .NET Framework.
  • SampleProject - Sample project for .NET Framework.
  • SourceGridMini (SourceGridMini.dll) - SourceGrid for .NET Compact Framework (Pocket PC).
  • SampleProjectMini - Sample project for .NET Compact Framework.

I have created 2 solutions: SourceGrid2.sln for the standard version and SourceGrid2Mini.sln for the Compact Framework version.

SourceGridMini for .NET Compact Framework

SourceGridMini is the version of SourceGrid for .NET Compact Framework. This version use the same code of the normal version but with some conditional code (#if MINI) for the unsupported features. Many editors are not supported, some mouse events (MouseEnter, MouseLeave, ...) and this project reference SourceLibraryMini.

There is still a lot of work for SourceGridMini, I must optimize drawing methods and is necessary check and test many features to create a stable and working version.

I have used Microsoft Pocket PC 2003 Emulator as test platform.

When you first open the Visual Studio solution an error will appear to tell you that Visual Studio cannot find SourceLibraryMini.dll. This is caused by the references of the projects for Pocket PC that cannot be relative to the current project, and so Visual Studio cannot find the reference to SourceLibraryMini.
To solve this problem all you have to do is to add a new reference to SourceLibraryMini.dll (that you can find in the References folder) to the project SourceGridMini and SampleProjectMini.

From now the article will refer to .NET Framework version, but the majority of code and explanations are valid also for .NET Compact Framework. In the future I will try to differentiate the two version.

Controls

There are 2 controls inside the SourceGrid2.dll assembly that can be inserted in the Toolbox of Visual Studio and used in any Form:

  • GridVirtual - A grid of virtual cells (ICellVirtual).
  • Grid - A grid of real cells (ICell).

There are therefore two fundamentally distinct objects: virtual cells and real cells. Virtual cells are cells that determine the appearance and the behavior of the cell but don't contain the value. The real cells have the same properties as virtual cells but also contain the value of the cell, and are therefore associated to a specific position in the grid.

Every cell is composed of three fundamental parts:

  • DataModel : The DataModel is the class that manages the value of the cells. It converts the value of the cell to a string for visual representation, create the editor of the cell and validate the inserted values.
  • VisualModel : The VisualModel is the class that draws the cell and contains the visual properties.
  • BehaviorModel : The BehaviorModel is the class that supplies the behavior of the cell.

This subdivision grants great flexibility and reusability of code, saves time and supplies a solid base for every type of customization.
For the more common cases there are some classes already arranged and configured, but with a few lines of code is possible to create personalized cells (see the next paragraphs for the details).

Grid

The Grid control is the ideal if you want the greatest flexibility and simplicity but without many cells. In fact, in this control every cell is represented by a .NET class and therefore occupies a specific quantity of resources. Moreover this is the only grid that supports features of RowSpan and ColumnSpan (cells merge).

After inserting the control in the form we can begin to write our code using the grid. For example in the Load event of the form we can write this code:

grid1.Redim(2, 2);
grid1[0,0] = new SourceGrid2.Cells.Real.Cell("Hello from Cell 0,0");
grid1[1,0] = new SourceGrid2.Cells.Real.Cell("Hello from Cell 1,0");
grid1[0,1] = new SourceGrid2.Cells.Real.Cell("Hello from Cell 0,1");
grid1[1,1] = new SourceGrid2.Cells.Real.Cell("Hello from Cell 1,1");

The previous code creates a table with 2 lines and 2 columns (Redim method) and populates every position with a cell. I have used the SourceGrid2.Cells.Real namespace wich contains the definition for real cells.

Every cells contains all the necessary display properties For example to change the background color of a cell we can write:

SourceGrid2.Cells.Real.Cell l_Cell = new SourceGrid2.Cells.Real.Cell("Custom back color");
l_Cell.BackColor = Color.LightGreen;

These are the main visual properties of a cell (for an entire list consult the documentation of the grid): BackColor, ForeColor, Border, Font, TextAlignment, WordWrap ...,

Now we try to create an entire grid with headers, automatic sort, resize of the columns with the mouse, string and DateTime editors and a checkbox.

grid1.BorderStyle = BorderStyle.FixedSingle;
grid1.ColumnsCount = 3;
grid1.FixedRows = 1;
grid1.Rows.Insert(0);
grid1[0,0] = new SourceGrid2.Cells.Real.ColumnHeader("String");
grid1[0,1] = new SourceGrid2.Cells.Real.ColumnHeader("DateTime");
grid1[0,2] = new SourceGrid2.Cells.Real.ColumnHeader("CheckBox");
for (int r = 1; r < 10; r++)
{
	grid1.Rows.Insert(r);
	grid1[r,0] = new SourceGrid2.Cells.Real.Cell("Hello " + r.ToString(), typeof(string));
	grid1[r,1] = new SourceGrid2.Cells.Real.Cell(DateTime.Today, typeof(DateTime));
	grid1[r,2] = new SourceGrid2.Cells.Real.CheckBox(true);
}
grid1.AutoSizeAll();

In the previous code I have set the grid border, the number of columns, the number of fixed rows and created the first header row. For the header I have used a ColumnHeader cell. With a simple loop I have created the other cells using a specific type for each column. The Cell class automatically creates an appropriate editor for the type specified (in this case a TextBox and a DateTimePicker). For the last column I have used a CheckBox cell that allows the display of a checkbox directly in the cell.
The form should look like the one in the following picture. This example is also present in the project SampleProject in the ZIP file.

To read a specific value of the grid you can use the value property of the cell like this: object val = grid1[1,0].Value;.

GridVirtual

The GridVirtual control is ideal when it is necessary to visualize a lot of cells and you already have available structured data like a DataSet, an Array, a document XML or other data structure.
This type of grid have the same features of the Grid control except for the automatic sort (this because the grid cannot automatically order any external data structure without copying its content) and the feature of RowSpan and ColumnSpan that allows spanning of a cell across other adjacent cells.
Another disadvantage is that creating a virtual grid is a little more difficult.

The main concept in a virtual grid is that the cells do not contain values, but read and write the value from an external data structure. This idea was implemented with an abstract class CellVirtual in which is necessary to redefine the methods GetValue and SetValue.
To use GridVirtual it is therefore necessary to create a class that derives from CellVirtual and personalizes the reading of the data source chosen.
Also it is usually better to create a control that derives from GridVirtual and overrides the method GetCell in order to have greater flexibility and more solid code.
If you prefer you can use the GridVirtual control directly and use the event GettingCell. The purpose of the method GetCell or the event GettingCell is to return, for a given position (row and column), the chosen cell. This allows great flexibility because you can return any ICellVirtual for a specific type; for example you could return a cell of type header when the row is 0.

In the following example I have created a virtual grid that reads and writes the values in an array. First I have inserted the control GridVirtual in a form, then I have written the code that defines the virtual class deriving from CellVirtual:

public class CellStringArray : SourceGrid2.Cells.Virtual.CellVirtual
{
	private string[,] m_Array;
	public CellStringArray(string[,] p_Array):base(typeof(string))
	{
		m_Array = p_Array;
	}
	public override object GetValue(SourceGrid2.Position p_Position)
	{
		return m_Array[p_Position.Row, p_Position.Column];
	}
	public override void SetValue(SourceGrid2.Position p_Position, object p_Value)
	{
		m_Array[p_Position.Row, p_Position.Column] = (string)p_Value;
		OnValueChanged(new SourceGrid2.PositionEventArgs(p_Position, this));
	}
}

In the previous code I have created a virtual cell with an editor of type string that reads and writes the values in an array specified in the constructor.
After the call to the SetValue method we should call the OnValueChanged method to notify the grid to update this cell.

In the event Load of the Form I insert this code:

private void frmSample15_Load(object sender, System.EventArgs e)
{
	gridVirtual1.GettingCell += new SourceGrid2.PositionEventHandler(gridVirtual1_GettingCell);
	gridVirtual1.Redim(1000,1000);
	string[,] l_Array = new string[gridVirtual1.RowsCount, gridVirtual1.ColumnsCount];
	m_CellStringArray = new CellStringArray(l_Array);
	m_CellStringArray.BindToGrid(gridVirtual1);
}

I have added an event handler to GettingCell event, created the grid and the array with 1000 rows and 1000 columns, created a new instance of the previously defined virtual cell class CellStringArray and with the method BindToGrid I have linked the cell to the grid.
I have created a single virtual cell, m_CellStringArray, that will be used for every position of the matrix. It is always necessary to call the method BindToGrid on the cells that we want to use in a virtual grid.

In order to finish we should write the method GettingCell and store the variable for the cell:

private CellStringArray m_CellStringArray;
private void gridVirtual1_GettingCell(object sender, SourceGrid2.PositionEventArgs e)
{
	e.Cell = m_CellStringArray;
}

The result should look like the one in the following picture. This example is also present in the project SampleProject included in the ZIP file.

VisualModel

Namespace: SourceGrid2.VisualModels

Every cell has a property VisualModel that returns an interface of type IVisualModel. The cell uses this interface to draw and to customize the visual properties of the cell.

The purpose of the VisualModel is to separate the drawing code from the rest of the code and allows sharing of the same visual model between cells. In fact the same instance of VisualModel can be used on many cells simultaneously thus optimizing the use of the resources of the system.
The default VisualModel classes are read-only, however each VisualModel is provided with a Clone method that allows you to create identical instances of the same model.

These are the default VisualModel classes in the namespace SourceGrid2.VisualModels:

  • SourceGrid2.VisualModels.Common - Used for classic cells. In this model you can customize the colors, the font, the borders and a lot other properties.
  • SourceGrid2.VisualModels.CheckBox* - Used for checkbox style cells. The checkbox can be selected, disabled and can contain a caption.
  • SourceGrid2.VisualModels.Header* - Used for header style cells with 3D borders. You can configure the borders to gradually vanish from the color of the border to the color of the cell for a better three-dimensional effect.
  • SourceGrid2.VisualModels.MultiImages - Allows the drawing of more than one image in the cell.

*The VisualModel marked with an asterisk requires a special interface to work correctly, for example the CheckBox model needs a cell that supports the ICellCheckBox interface.

Each of these classes contains one or more static properties with some convenient default read-only instances:

  • SourceGrid2.VisualModels.Common.Default
  • SourceGrid2.VisualModels.Common.LinkStyle
  • SourceGrid2.VisualModels.CheckBox.Default
  • SourceGrid2.VisualModels.CheckBox.MiddleLeftAlign
  • SourceGrid2.VisualModels.Header.Default
  • SourceGrid2.VisualModels.Header.ColumnHeader
  • SourceGrid2.VisualModels.Header.RowHeader

This code shows how to assign the same VisualModel to more cells previously created and then change some properties:

SourceGrid2.VisualModels.Common l_SharedVisualModel = new SourceGrid2.VisualModels.Common();
grid1[0,0].VisualModel = l_SharedVisualModel;
grid1[1,0].VisualModel = l_SharedVisualModel;
grid1[2,0].VisualModel = l_SharedVisualModel;
l_SharedVisualModel.BackColor = Color.LightGray;

Consider also that when you write Cell.BackColor the property automatically calls the BackColor property of the associated VisualModel. To facilitate the utilization of the more common properties, if you write Cell.BackColor = Color.Black; the cell automatically clones the current VisualModel, changes the backcolor to the cloned instance and assigns the cloned instance again to the cell.

DataModel

Namespace: SourceGrid2.DataModels

To represent the value of a cell in a string format and to supply a cell data editor, it is necessary to populate the property DataModel of the cell. If this property is null it is not possible to change the value of the cell and the string conversion will be a simple ToString of the value.

Usually a DataModel uses a TypeConverter of the requested type to manage the necessary conversion, particularly the string conversion (used to represent the cell value).

These are the default DataModel classes in the namespace SourceGrid2.DataModels:

  • DataModelBase - Supplies the methods of conversion and allows the alteration of the cell value by code only; it does not supply graphic interface. This class is the base of all the other editors and is used also to manage read-only cells but with customized formattings or special editors (for example the CheckBox cell uses a read-only editor because the value is changed clicking directly on the checkbox).
  • EditorControlBase - Abstract class that helps to use a control as editor for the cell.
  • EditorTextBox - A TextBox editor. This is one of the more used editor by all types that support string conversion (string, int, double, enum,....)
  • EditorComboBox - A ComboBox editor.
  • EditorDateTime - A DateTimePicker editor.
  • EditorNumericUpDown - A NumericUpDown editor.
  • EditorTextBoxButton - A TextBox editor with a button to open a details form.
  • EditorUITypeEditor - Supplies the editing of the cell of all types that have an UITypeEditor. A lot of types support this class: DateTime, Font, a lot of enum, ...

A DataModel can be shared between many cells; for example you can use the same DataModel for every cell of a column.

To create an editable cell there are 3 possibilities:

  • Create the cell specifying the value type. In this manner the cell automatically calls an utility function, Utility.CreateDataModel, that returns a valid DataModel for the type specified or null.
    grid1[0,0] = new SourceGrid2.Cells.Real.Cell("Hello", typeof(string));
  • Create the DataModel separately and then assign it to the cells:
    SourceGrid2.DataModels.IDataModel l_SharedDataModel =
    	 SourceGrid2.Utility.CreateDataModel(typeof(string));
    grid1[0,0].DataModel = l_SharedDataModel;
    grid1[1,0].DataModel = l_SharedDataModel;
    This method is recommended when you want to use the same editor for several cells.
  • Create manually the correct editor and then assign it to the cells:
    SourceGrid2.DataModels.EditorTextBox l_TextBox =
    	 new SourceGrid2.DataModels.EditorTextBox(typeof(string));
    grid1[2,0].DataModel = l_TextBox;
    Also with this method is possible to share the same editor between many cells.

If you need greater control on the type of editor or there are special requirements I suggest to manually create the editor.
In this case, for example, I manually create the class EditorTextBox and then use the properties MaxLength and CharacterCasing.

SourceGrid2.DataModels.EditorTextBox l_TextBox =
	 new SourceGrid2.DataModels.EditorTextBox(typeof(string));
l_TextBox.MaxLength = 20;
l_TextBox.AttachEditorControl(grid1.ScrollablePanel);
l_TextBox.GetEditorTextBox(grid1.ScrollablePanel).CharacterCasing = CharacterCasing.Upper;
grid1[2,0].DataModel = l_TextBox;

Some properties are defined to a DataModel level, while others to an editor control level; in this case the property CharacterCasing is defined to a TextBox control level. To use these properties, it is necessary therefore to force a linking of the editor to the grid with the method AttachEditorControl and then call the method GetEditorTextBox to return the instance of the TextBox.
This mechanism is also useful for creating a special editor like the ComboBox editor. To insert a ComboBox you must write this code:

SourceGrid2.DataModels.EditorComboBox l_ComboBox = new SourceGrid2.DataModels.EditorComboBox(
								typeof(string),
								new string[]{"Hello", "Ciao"},
								false);
grid1[3,0].DataModel = l_ComboBox;

Of course it is possible to create a custom DataModel editor with custom control or special behaviors.
The following picture shows most of the editors available and some options like image properties:

BehaviorModel

Namespace: SourceGrid2.BehaviorModels

Every cell has a collection of BehaviorModel objects that you can read with the Behaviors property. A BehaviorModel is a class that characterizes the behavior of the cell. A model can be shared between many cells and allows great flexibility and simplicity for any new feature.

These are the default classes of type BehaviorModel:

  • SourceGrid2.BehaviorModels.Common - Common behavior of a cell.
  • SourceGrid2.BehaviorModels.Header - Behavior of a header.
  • SourceGrid2.BehaviorModels.RowHeader - Behavior of a row header, with resize feature.
  • SourceGrid2.BehaviorModels.ColumnHeader* - Behavior of a column header, with sort and resize feature. (need ICellSortableHeader)
  • SourceGrid2.BehaviorModels.CheckBox* - Behavior of a CheckBox. (need ICellCheckBox)
  • SourceGrid2.BehaviorModels.Cursor* - Allows the linking of a cursor to a specific cell. (need ICellCursor)
  • SourceGrid2.BehaviorModels.Button - Behavior of a Button.
  • SourceGrid2.BehaviorModels.Resize - Allows a cell to be resized with the mouse (this model is automatically used by header models).
  • SourceGrid2.BehaviorModels.ToolTipText* - Links the showing of a ToolTipText to a cell. (need ICellToolTipText)
  • SourceGrid2.BehaviorModels.Unselectable - Blocks a cell from receiving the focus.
  • SourceGrid2.BehaviorModels.ContextMenu* - Links the showing of a contextmenu to a cell. (need a ICellContextMenu)
  • SourceGrid2.BehaviorModels.CustomEvents - Exposes a list of events that you can use without deriving from a BehaviorModel.
  • SourceGrid2.BehaviorModels.BindProperty - Allows the linking of the value of a cell to an external property.
  • SourceGrid2.BehaviorModels.BehaviorModelGroup - Allows the creation of a BehaviorModel that automatically calls a list of BehaviorModel; useful when a bahavior needs other behaviors to work correctly.

*The BehaviorModel marked with an asterisk need special cells to complete their tasks, for example the class CheckBox requires that the cell supports the interface ICellCheckBox.

Every class have some static properties that return a default instance of the class:

  • SourceGrid2.BehaviorModels.Common.Default
  • SourceGrid2.BehaviorModels.Button.Default
  • SourceGrid2.BehaviorModels.CheckBox.Default
  • SourceGrid2.BehaviorModels.ColumnHeader.SortHeader
  • SourceGrid2.BehaviorModels.ColumnHeader.ResizeHeader
  • SourceGrid2.BehaviorModels.ColumnHeader.SortResizeHeader
  • SourceGrid2.BehaviorModels.ColumnHeader.NoSortNoResizeHeader
  • SourceGrid2.BehaviorModels.ColumnHeader.Default
  • SourceGrid2.BehaviorModels.Cursor.Default
  • SourceGrid2.BehaviorModels.Header.Default
  • SourceGrid2.BehaviorModels.Resize.ResizeHeight
  • SourceGrid2.BehaviorModels.Resize.ResizeWidth
  • SourceGrid2.BehaviorModels.Resize.ResizeBoth
  • SourceGrid2.BehaviorModels.RowHeader.Default
  • SourceGrid2.BehaviorModels.ToolTipText.Default
  • SourceGrid2.BehaviorModels.Unselectable.Default

In the following code example I create a BehaviorModel that changes the backcolor of the cell when the user moves the mouse over the cell.

public class CustomBehavior : SourceGrid2.BehaviorModels.BehaviorModelGroup
{
	public override void OnMouseEnter(SourceGrid2.PositionEventArgs e)
	{
		base.OnMouseEnter (e);
		((SourceGrid2.Cells.Real.Cell)e.Cell).BackColor = Color.LightGreen;
	}
	public override void OnMouseLeave(SourceGrid2.PositionEventArgs e)
	{
		base.OnMouseLeave (e);
		((SourceGrid2.Cells.Real.Cell)e.Cell).BackColor = Color.White;
	}
}

To use this BehaviorModel insert this code into Load event of a form:

grid1.Redim(2,2);

CustomBehavior l_Behavior = new CustomBehavior();
for (int r = 0; r < grid1.RowsCount; r++)
	for (int c = 0; c < grid1.ColumnsCount; c++)
	{
		grid1[r,c] = new SourceGrid2.Cells.Real.Cell("Hello");
		grid1[r,c].Behaviors.Add(l_Behavior);
	}

Cells

Namespace: SourceGrid2.Cells

These are the default cells available:

  • SourceGrid2.Cells.Virtual - This namespace contains all the virtual cells that can be used with a GridVirtual control. These are all abstract cells and you must derive from these cells to use your custom data source.
    • CellVirtual - Base cell for each other implementations; use for the most common type of virtual cells.
    • Header - A header cell.
    • ColumnHeader - A column header cell.
    • RowHeader - A row header cell.
    • Button - A button cell.
    • CheckBox - A checkbox cell.
    • ComboBox - A combobox cell.
    • Link - A link style cell.
  • SourceGrid2.Cells.Real - This namespace contains all the real cells that can be used with a Grid control.
    • Cell - Base cell for all other implementations; use for the most common type of real cells.
    • Header - A header cell.
    • ColumnHeader - A column header cell.
    • RowHeader - A row header cell.
    • Button - A button cell.
    • CheckBox - A checkbox cell.
    • ComboBox - A combobox cell.
    • Link - A link style cell.

The goal of these classes is to simplify the use of VisualModel, DataModel and BehaviorModel. If we look at the code of any of the cell classes we can see that these classes use these models according to the role of the cell. There are however models that require special interfaces and in this case the cells implements all the required interfaces.
This is, for example, the code of the cell SourceGrid2.Cells.Real.CheckBox:

public class CheckBox : Cell, ICellCheckBox
{
	public CheckBox(string p_Caption, bool p_InitialValue)
	{
		m_Caption = p_Caption;

		DataModel = new SourceGrid2.DataModels.DataModelBase(typeof(bool));
		VisualModel = SourceGrid2.VisualModels.CheckBox.MiddleLeftAlign;
		Behaviors.Add(BehaviorModels.CheckBox.Default);
		
		Value = p_InitialValue;
	}
	public bool Checked
	{
		get{return GetCheckedValue(Range.Start);}
		set{SetCheckedValue(Range.Start, value);}
	}
	private string m_Caption;
	public string Caption
	{
		get{return m_Caption;}
		set{m_Caption = value;}
	}
	public virtual bool GetCheckedValue(Position p_Position)
	{
		return (bool)GetValue(p_Position);
	}
	public virtual void SetCheckedValue(Position p_Position, bool p_bChecked)
	{
		if (DataModel!=null && DataModel.EnableEdit)
			DataModel.SetCellValue(this, p_Position, p_bChecked);
	}
	public virtual CheckBoxStatus GetCheckBoxStatus(Position p_Position)
	{
		return new CheckBoxStatus(DataModel.EnableEdit, GetCheckedValue(p_Position), m_Caption);
	}
}

As you can see the CheckBox class simply use the models SourceGrid2.DataModels.DataModelBase(typeof(bool)), SourceGrid2.VisualModels.CheckBox.MiddleLeftAlign and BehaviorModels.CheckBox.Default. CheckBox implements the ICellCheckBox interface with its method GetCheckBoxStatus.
The methods Checked, Caption, GetCheckedValue and SetCheckedValue are methods to simplify the editing of the value of the cell.

Structure of the Grid

Rows and Columns

The main components of a grid are the rows and the columns. To manipulate this information, SourceGrid supplies 2 properties:

  • Rows - Returns a collection of type RowInfoCollection that is a list of RowInfo objects.
  • Columns - Returns a collection of type ColumnInfoCollection that is a list of ColumnInfo objects.

These are some of the RowInfo class properties: Height, Top, Bottom, Index, Tag. In contrast, these are the properties of the ColumnInfo class:Width, Left, Right, Index, Tag.

There are many ways to create rows and columns:

  • grid1.Redim(2,2);
  • grid1.RowsCount = 2;
    grid1.ColumnsCount = 2;
  • grid1.Rows.Insert(0);
    grid1.Rows.Insert(1);
    grid1.Columns.Insert(0);
    grid1.Columns.Insert(1);

These three examples perform all the same task of creating a table with 2 rows and 2 columns.

To change the width or the height of a row or a column you can use this code:

grid1.Rows[0].Height = 100;
grid1.Columns[0].Width = 100;

The properties Top, Bottom, Left and Right are automatically calculated using the width and the height of the rows and columns.

Panels

To correctly manage scrollbars, fixed columns and rows and a lot other details, the grid has an internal panel structure like this:

  • 1) TopLeftPanel - Keeps fixed row and fixed column cells.
  • 2) TopPanel - Keeps fixed rows.
  • 3) LeftPanel - Keeps fixed columns.
  • 4) ScrollablePanel - Keeps all cells that are not fixed.
  • 5) HScrollBar - Horizontal ScrollBar
  • 6) VScrollBar - Vertical ScrollBar.
  • 7) BottomRightPanel - Panel to manage the small space between the two scrollbars.

Events

The mouse and keyboard events can be used with a BehaviorModel or can be connected directly to the grid.
All the events are fired first to the panels and then automatically moved to the GridVirtual or Grid control. To use these events you can write this code:

grid.MouseDown += new System.Windows.Forms.MouseEventHandler(grid_MouseDown);

This can be done also with the Visual Studio designer.
Look at the example 8 in the project SampleProject for details.

ContextMenu

The grid has a default ContextMenu that can be customized with the ContextMenuStyle property. It is possible to connect a ContextMenu to the Selection object with the Grid.Selection.ContextMenuItems, that will be used for all selected cells. Alternately you can connect a ContextMenu directly to a specific cell.
Look at the example 10 in the project SampleProject for further details.

Focus and Selection

A cell can be selected or can have the focus. Only one cell can have the focus, identified by the Grid.Selection.FocusPosition property of the grid, while many cells can be selected. A cell is selected when is present in the Selection object of the grid.
The cell with the focus receives all of the mouse and keyboard events, while the selected cells can receive actions like copy,paste and clear.

To set the focus on a specific cell you can use Grid.Selection.Focus(Position pos) method, using for input the position of the cell that will receive the focus, use Position.Empty as parameter to remove the focus.

To list all the selected cells you can use Grid.Selection.GetCellsPositions() method, that retuns all the selected position. You can customie many aspects of the selection with these properties: Grid.Selection.BackColor, Grid.Selection.Border, Grid.Selection.BorderMode, Grid.Selection.FocusBackColor and Grid.Selection.MaskStyle.

You can also use these events to responde to specific action of the user: Grid.Selection.FocusRowEntered, Grid.Selection.FocusRowLeaving, Grid.Selection.FocusColumnEntered, Grid.Selection.FocusColumnLeaving, Grid.Selection., Grid.Selection.CellGotFocus, Grid.Selection.CellLostFocus.

Position and Range

Two of the most used objects in the project SourceGrid are the struct Position and Range. The struct Position identifies a position with a Row and a Column, while the struct Range identifies a group of cells delimited from a start Position and an end Position.

Performance

To optimize performance of this control use the GridVirtual control when it is necessary to display a lot of cells and always try to share the models (DataModel, VisualModel, BehaviorModel) between as many cells as possible.
The performance of the grid is quite good even if the drawing code can be still optimized, especially when scrolling.
Consult the project SampleProject for further information on the performance of the grid.

Extensions

The project SampleProject presents a lot of examples and snippets of code that provide ideas or suggestions on how to implement custom grids. In particular, the folder Extensions presents some grids that supply functionality like the binding to a DataSet (DataTable), to an Array and to an ArrayList. There is also a grid used for a planning sample.

Screenshots

How To

Insert this grid control in the Visual Studio .NET ToolBox:

  • In the projects References add SourceGrid2.dll and SourceLibrary.dll.
  • Open a form.
  • Open the toolbox , right-click and select "Add/Remove Items...".
  • Click Browse and select SourceGrid2.dll file.
  • You can now drag the grid control from the toolbox to your forms.

How to select an entire row:

grid1.Rows[1].Select = true;

How to select all the cells:

grid1.Selection.AddRange(grid1.CompleteRange);

How to create an editor with advanced validation rule:

grid1[0,0] = new SourceGrid2.Cells.Real.Cell(2, typeof(int));
grid1[0,0].DataModel.MinimumValue = 2;
grid1[0,0].DataModel.MaximumValue = 8;
grid1[0,0].DataModel.DefaultValue = null;
grid1[0,0].DataModel.AllowNull = true;

How to create a ComboBox editor to display a value different from the real used value, in this case is displayed a string while the real value is a double.

double[] l_RealValues = new double[]{0.0,0.5,1.0};
SourceGrid2.DataModels.EditorComboBox l_EditorCombo =
	 new SourceGrid2.DataModels.EditorComboBox(typeof(double));
l_EditorCombo.StandardValues = l_RealValues;
l_EditorCombo.StandardValuesExclusive = true;
l_EditorCombo.AllowStringConversion = false;
SourceLibrary.ComponentModel.Validator.ValueMapping l_Mapping =
	 new SourceLibrary.ComponentModel.Validator.ValueMapping();
l_Mapping.ValueList = l_RealValues;
l_Mapping.DisplayStringList = new string[]{"Zero", "One Half", "One"};
l_Mapping.BindValidator(l_EditorCombo);
grid1[0,0] = new SourceGrid2.Cells.Real.Cell(0.5, l_EditorCombo);

How to catch a click event on a cell using C#:

//Create a behavior that you can use with any cells
SourceGrid2.BehaviorModels.CustomEvents behaviorClick = new SourceGrid2.BehaviorModels.CustomEvents();
behaviorClick.Click += new SourceGrid2.PositionEventHandler(behaviorClick_Click);

grid1[0,0] = new SourceGrid2.Cells.Real.Cell("Ciao");
grid1[0,0].Behaviors.Add(behaviorClick);
..............
private void behaviorClick_Click(object sender, SourceGrid2.PositionEventArgs e)
{
	MessageBox.Show(e.Position.ToString());
}

How to catch a click event on a cell using VB.NET:

'Create a behavior that you can use with any cells
Dim behaviorClick As SourceGrid2.BehaviorModels.CustomEvents = New SourceGrid2.BehaviorModels.CustomEvents
AddHandler behaviorClick.Click, AddressOf cell_click

Grid1.Item(0, 0) = New SourceGrid2.Cells.Real.Cell("Ciao")
Grid1.Item(0, 0).Behaviors.Add(behaviorClick)
.............
Private Sub cell_click(ByVal sender As System.Object, ByVal e As SourceGrid2.PositionEventArgs)
	MessageBox.Show(e.Position.ToString())
End Sub

Features

What SourceGrid can do:

  • It is possible to customize the graphic appearance, the type of editor and the behavior (cursor, tooltiptext, contextmenu ...,) of every cell.
  • Natively supports all of the types of data that have a TypeConverter or an UITypeEditor associated.
  • Any .NET control can be used like an editor with few lines of code.
  • You can insert, delete and move rows and columns.
  • The height and the width can be customized independently for every columns and rows or can be calculated automatically based to the content of the cells.
  • Supports features of RowSpan and ColumnSpan to merge cells.
  • Supports automatic operations of Copy and Paste.
  • Natively supports column sort.
  • You can change the width and the height of the columns and rows.
  • In every cell is possible to customize the image and the alignment of the text and the image.
  • Supports MultiLine and WordWrap text.
  • Supports an HTML export.
  • With some extension, supports data binding features.
  • Support virtual cells used for binding to any type of data source.
  • Mask edit support (characters validation).

... and what cannot do

  • SourceGrid doesn't have a designer, everything must be done with code.
  • No printing support.
  • No grouping or tree view support.

Change SourceGrid code

You are allowed to change, recompile and to distribute the control SourceGrid for private and commercial use, I ask only to maintain the Copyright notes at the end of the page.
I recommend changing the file SourceGrid2.snk with a personalized version, so you do not have problems with compatibility with different versions of the control. Consult MSDN for further information: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cptutorials/html/_4__A_Shared_Component.asp

Future developments

  • Enhancement of the drawing code.
  • Enhancement of SourceGridMini for .NET Compact Framework.
  • Column dragging.
  • Native drag and drop.

Known problems

Previous versions

Version 2 of SourceGrid introduced many changes, and it is not possible to list them all. The manner of utilization is very similar, but it is not simple to convert a code written with previous versions.
These are some suggestions:

  • The major features of the grid only work using the ICellVirtual interface; they no longer work with the Cell class. The interface now contains only the necessary methods and therefore is leaner than Cell class. What the code would have been before:
    grid[0,0] = new SourceGrid.Cell("Ciao");
    grid[0,0].BackColor = Color.White;
    now should be transformed to
    SourceGrid2.Cells.Real.Cell l_Cell = new SourceGrid2.Cells.Real.Cell("Ciao");
    l_Cell.BackColor = Color.White;
    grid[0,0] = l_Cell;
  • In the prior version the base cell was derived from the Cell class; now it is the interface ICellVirtual. The major change of this class is that it does not contain information about the position (row and column).
  • A lot of the methods of the grid that previously used the Cell type now use the Position type; it is however possible to extract the interface ICellVirtual (and then cast to more specific interfaces) given a struct Position and using the method Grid.GetCell.
  • Now the grid natively supports the property BorderStyle which eliminated the Panel requirement that was necessary before to create a border.
  • All of the code that was first bound to the events of a cell now must be moved to a BehaviorModel; you can use the SourceGrid2.BehaviorModels.CustomEvents as an example.
  • The Selection object is no longer a collection of cells but a collection of Range.
  • With the addition of the Rows and Columns objects, the code that works on rows and columns is now simpler; a lot of methods that were previously in the grid now are in the RowInfoCollection, RowInfo, ColumnInfoCollection or ColumnInfo classes.
  • The object CellsContainer is no longer present, and was logically replaced with the panels; the more common events are linked directly to the grid and therefore the code that previously used CellsContainer now can use directly the Grid control.
  • The old object ICellModel is now the object IDataModel, while the object VisualProperties is now IVisualModel.
  • For now, the class CellControl is no longer supported; in future I may introduce it again.

The previous versions of this control and further information can be found at http://www.devage.com/

History

2.2.0.0 (09 March 2005)

Documentation Improvements

  • Added informations for a known problem when using the grid inside a MDI child form.

2.2.0.0 (06 August 2004)

Bugs Fixing

  • Fixed a bug when calling Grid.CustomScrollPosition with positive value.
  • Fixed a bug when set the focus on the same cell that already has the focus and with the SelectionMode = Row/Column
  • Fixed the alignment of the text inside the editors when changing the size of the Cell.
  • Fixed sample 12 for Date parsing and formatting, and fixed XML file format.

Improvements

  • Improved Sample 17
  • Moved all the Focus methods inside the Selection object
    • Grid.SetFocusCell -> Grid.Selection.Focus
    • Grid.CellGotFocus -> Grid.Selection.CellGotFocus
    • Grid.CellLostFocus -> Grid.Selection.CellLostFocus
    • Grid.FocusCellPosition -> Grid.Selection.FocusPosition
    • Grid.FocusRow -> Grid.Selection.FocusRow
    • Grid.FocusColumn -> Grid.Selection.FocusColumn
    • Grid.FocusStyle -> Grid.Selection.FocusStyle (note that usually the Form Designer set this property to None. In this case you can simply remove this line because None is the default.)
  • Removed DrawCellStatus parameters and enum definitaion from the draw methods of the VisualModels because are no more used. If you need a special VisualModel that draw itself in a different manner when selected or with the focus you can manually check the status of the cell.
  • Now when sorting all the columns (for example using a column header) the sort move also the RowInfo object. This is expecially usefull when using a Tag property of a RowInfo (grid.Rows[r].Tag).
  • Changed parameters for events CellGotFocus and CellLostFocus to CellGotFocusEventArgs and CellLostFocusEventArgs.

New Features

  • New sample 23 to show some visual style of the grid and how to insert and remove rows.
  • SelectionBorderMode.FocusCell enum that lets you to draw a border alwyas around the cell with the focus. See sample 17 for an example.
  • Grid.Selection.FocusBackColor property to customize the BackColor mask of the Focus cell. To customize the border you must use the Selection.Border with Selection.BorderMode = FocusCell. See Sample 17 for details.
  • Grid.Selection.MaskStyle property used to customize the visual style of the selection mask. For example you can disable the selection of unitialized cells. See Sample 17 for details.
  • ColumnInfo.ColumnsRemoving event (Grid.Columns[c].ColumnsRemoving), fired before a column is removed.
  • RowInfo.RowsRemoving event (Grid.Rows[r].RowsRemoving), fired before a row is removed.
  • Grid.Selecection.FocusRowLeaving, FocusRowEntered, FocusColumnLeaving, FocusColumnEntered fired when changing the focused column/row.

2.1.0.0 (23 July 2004)

Bugs Fixing

  • Sorting GridDataTable when removing a column, in the SampleProject\Extension
  • Row/Column span when reading merged cells.
  • Editing cell with control characters like Escape, ...
  • NumericUpDown editor when pressing keybord keys to start edit
  • Calculation of the required size of a CheckBoxCell, now calculate the size using also the caption.
  • Fixed Range.Intersect method (used with the Invalidate method).
  • Deselect all the cells when click on the focus cell.
  • Selecting columns and rows using the column/row header.
  • Scrolling the cells using the mouse selection on Fixed Cells.
  • Fixed Worksheet grid when Clearing values
  • Fixed a bug when using Grid.LinkedControlsList.UseCellBorder == false property
  • Removing rows or columns that has the selection.
  • Disabled the selection of the entire row/column when Grid.Selection.EnableMultiSelection is false and the user click on a header
  • The BehaviorModel Button now is also unselectable (used in the Cells.Button).

Improvements

  • Improved Selection visual style and methods. Removed VisualModel.SelectionForeColor, SelectionBackColor, FocusForeColor, FocusBackCOlor, SelectionBorder, FocusBorder; replaced with the Selection.BackColor. Now the selection is drawed as a transparent color on the normal cells. If you want to customize the FocusBackColor or ForeColor you must write a custom VisualModel.
  • Removed not used enum CommonBorderStyle from the header and removed the enum definition.
  • Replaced method RectangleBorder.FormatBorder with RectangleBorder.CreateInsetBorder and RectangleBorder.CreateRaisedBorder
  • Removed Debug.Assert when editing failed
  • Added a sample to show how to create a ListBox with checkbox using SourceGrid, see "Sample 16".
  • Removed the PocketPC projects from the default solution. You can alwyays use PocketPC projects separately.
  • Changed to method to draw border to a custom method for a bug in ControlPaint.DrawBorder
  • Renamed method Panel.PositionAtPoint to PositionAtPointGrid
  • Removed method Utility.PaintImageAndText replaced with GridPaint.DrawImageAndText
  • Reviewed AutoSize methods: replaced "Grid.AutoSizeColumn/Row" with "Grid.Columns/Rows[x].AutoSize", renamed "Grid.AutoSizeAll" and "Grid.AutoSizeRange" with "Grid.AutoSize". Replaced all the parameters MinHeight and MinWidth with Grid.AutoSizeMinHeght and AutoSizeMinWidth.
  • Improved Sample 17 to show the new Selection BackColor and border features, like transparent support and selection border.
  • Removed Cell.EditableMode and Cell.EnableEdit. You must use Cell.DataModel.EditableMode and Cell.DataModel.EnableEdit and fixed the samples to use this new properties.
  • Improved Sample 3 to show custom format and trim features

New Features

  • Added class VisualModels.FlatHeader and a checkbox to try this model in the Sample 1
  • Added Grid.SortedRangeRows event, called when sort operation is completed. Can be used with SortingRangeRows to customize sort operations or for special sort code. Added in the Sample 8 the test for these events.
  • Added enum values SourceGrid2.GridSpecialKeys.Control and SourceGrid2.GridSpecialKeys.Shift to enable or disable "control" key and "shift" key for selection operations. You can use these values with the property Grid.SpecialKeys. With these enums you can disable the selection of non adjacent cells. Look at the "Sample 16" for an example.
  • Added Selection.Border to draw a border all around the selected cells.
  • Added new static class for painting utilities GridPaint with methods: DrawBorder and DrawImageAndText
  • Added enum AutoSizeMode.MinimumSize to properties Grid.Columns.AutoSizeMode and Grid.Rows.AutoSizeMode to block autosize to a minimum width/height for a specified column/row. See Sample 22 for other features and demonstration.
  • New Sample 22 for AutoSize and Streatch features.
  • Compiled with SourceLibrary 1.2.3.0
  • Added CLSCompliant attribute
  • New DataModel SourceGrid2.DataModels.EditorTime for editing time value. See "Sample 3" for an example.

2.0.9.0 (4 May 2004)

  • Small corrections for ColumnHeader and for some examples.
  • Recompiled with SourceLibrary 1.2.2.0 for corrections on ComboBox editor when used with FormBorderStyle equal to FixedToolWindow.
  • Added method OnEditorAttached to class EditorControlBase. Redefine this method to customize the configuration of the editor control.
  • Many corrections on article contents (thanks to Michael Look).
  • Fixed a bug on Click event on cells with Row/Column span greater than 1.
  • Removede previous default value of the class SourceGrid2.BehaviorModels.ColumnHeader and replaced by: ResizeHeader, SortHeader, SortResizeHeader, NoSortNoResizeHeader, Default.
  • Removed compilation of project for CompactFramework on Debug and Release configurations. For Compact Framework use DebugMini or ReleaseMini.
  • Added sample on column and row span (sample 21).

2.0.7.0 (23 April 2004)

  • Many small changes for compatibility with .NET Compact Framework (SourceGridMini.dll).
  • Read the new sections "Projects and configurations" and "SourceGridMini for .NET Compact Framework" for some important notes.
  • Replaced enum System.Drawing.ContentAlignment with SourceLibrary.Drawing.ContentAlignment for compatibility with the Compact Framework.
  • Added some constructors on some cells class to customize the BehaviorModel used.
  • Fixed a bug in ColumnHeader or RowHeader when RowSpan/ColumnSpan was greater than 1.
  • Renamed method GetStartingPosition to PositionToStartPosition.
  • Added a feature to insert a BackGroundImage and a transparent background.
  • Added sample to show BackGorundImage feature.
  • Added sampel to show Drag/Drop feature.
  • Moved OnEditException from EditorControlBase class to DataModelBase and called also when SetCellBalue failed.
  • Fixed a bug to allow width and Heigth equal to 0.
  • Fixed a bug in the CustomScrollControl for scrollbars.
  • Changed reference to SourceLibrary 1.2.1.0.

2.0.4.0 (5 April 2004)

  • Fixed method SetFocusCell to check the Selection.EnableMultiSelection property with the Shift key.
  • Fixed RowInfo.Focus and ColumnInfo.Focus methods when FixedColumns or FixedRows are 0 and with ColumnHEader or RowHeader cells.
  • Fixed GetScrollPositionToShowCell methods when ColumnSpan is greater than 1 and the user click in a fixed cell.
  • Added sample 17 to show selection modes.
  • Added Extensions.PlanningGrid, look at sample 18.
  • Fixed all the editors when used with fixed cells.
  • Fixed methods GetCells and SetCells (called on row and column move) when called with row or column fixed.
  • Renamed RowInfo.GetCells and RowInfo.SetCells to RowInfo.Cells.
  • Renamed ColumnInfo.GetCells and ColumnInfo.SetCells to ColumnInfo.Cells.

2.0.3.0 (25 March 2004)

  • Moved and reviewed to the SourceLibrary project controls ComboBoxEx and TextBoxButton.

2.0.2.1 (24 March 2004)

  • Fixed a bug on Range class. When adding or removing rows ColumnSpan and RowsSpan informations were not preserved.

2.0.2.0 (23 March 2004)

  • Fixed a bug on ColumnHeader sort when used without FixedRows

2.0.1.0 (23 March 2004)

  • Divided interface IDataModel and partially moved to SourceLibrary.ComponentModel.Validator.
  • Renamed methods StringToObject to StringToValue and ObjectToString to ValueToString.
  • Now to prevent editing the textbox of a ComboBox editor now you must use property AllowStringConversion = false. StandardValuesExclusive property allows to insert only the values present in the StandardValueList.
  • Renamed method SupportStringConversion to IsStringConversionSupported().
  • Removed methods ExportValue and ImportValue.
  • EditorTextBox, EditorTextBoxButton e EditorComboBox now use TextBoxTyped control as textbox.
  • Added editor EditorTextBoxNumeric for input char validation for numeric data type.
  • AutoSize method for Header cell now add some extra space for sort icon.
  • Added properties Grid.Columns[0].AutoSizeMode and Grid.Rows[0].AutoSizeMode for prevent autosize and stretch for specific columns/rows.
  • Added properties Grid.Selection.SelectedRows and Grid.Selection.SelectedColumns that returns an array of selected rows or columns.
  • Added methods OnEditStarting and OnEditEnded to the cell and to BehaviorModel, called when editing start and end.
  • Renamed methods IDataModel.StartEdit to InternalStartEdit and EndEdit to InternalEndEdit because these are internal methods only, to start or end editing call Cell.StartEdit / Cell.EndEdit.
  • Renamed method DataModel.GetDisplayString to DataModel.ValueToDisplayString.
  • Added many examples for cell type and editors (See Sample 3).
  • Fixed a bug in AutoSize when called with no rows or columns.
  • Fixed a bug in SetFocusCell that don't put the focus in the grid.
  • Fixed a bug for MouseDown when in editing mode.

2.0.0.0 (15 March 2004)

  • First release of 2.0 version

License (BSD-style)

SourceGrid - .NET(C#) Grid control

Copyright (c) 2004, Davide Icardi

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of the ORGANIZATION nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

posted on 2005-07-04 16:08  秋雨飘飞  阅读(8766)  评论(0编辑  收藏  举报