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 [email protected] .
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.
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 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.
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).
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;
.
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.
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.
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:
Utility.CreateDataModel
, that returns a valid DataModel
for the type specified or null. grid1[0,0] = new SourceGrid2.Cells.Real.Cell("Hello", typeof(string));
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. 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:
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);
}
Namespace: SourceGrid2.Cells
These are the default cells available:
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. 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.
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.
To correctly manage scrollbars, fixed columns and rows and a lot other details, the grid has an internal panel structure like this:
TopLeftPanel
- Keeps fixed row and fixed column cells. TopPanel
- Keeps fixed rows. LeftPanel
- Keeps fixed columns. ScrollablePanel
- Keeps all cells that are not fixed. HScrollBar
- Horizontal ScrollBar VScrollBar
- Vertical ScrollBar. BottomRightPanel
- Panel to manage the small space between the two scrollbars. 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.
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.
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
.
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
.
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.
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.
Insert this grid control in the Visual Studio .NET ToolBox:
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
What SourceGrid can do:
TypeConverter
or an UITypeEditor
associated. RowSpan
and ColumnSpan
to merge cells. ... and what cannot do
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
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:
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;
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). 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
. BorderStyle
which eliminated the Panel requirement that was necessary before to create a border. BehaviorModel
; you can use the SourceGrid2.BehaviorModels.CustomEvents
as an example. Selection
object is no longer a collection of cells but a collection of Range
. 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. 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. ICellModel
is now the object IDataModel
, while the object VisualProperties
is now IVisualModel
. The previous versions of this control and further information can be found at http://www.devage.com/
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:
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.