Click here to Skip to main content
Click here to Skip to main content

SourceGrid - Open Source C# Grid Control

, 4 Aug 2013 MIT
Rate this:
Please Sign up or sign in to vote.
SourceGrid is a free open source grid control. Supports virtual grid, custom cells and editors, advanced formatting options and many others features

Sample Image - maximum width is 600 pixels

Introduction

SourceGrid is a Windows Forms control written entirely in C#, my goal is to create a simple but flexible grid to use in all of the cases in which 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 be customize or not compatible with. NET. The Microsoft DataGrid for me is too DataSet orientated and therefore results often complicated to use in the cases in which the source data isn't a DataSet and often is not enough customizable.

I want to thank Chirs Beckett, Anthony Berglas, Wayne Tanner, Ernesto Perales, Vadim Katsman, Jeffery Bell, Gmonkey, cmwalolo, Kenedy, zeromus, Darko Damjanovic, John Pierre and a lot of other persons who helped me with code, bugs report and with new ideas and suggestions.

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

The last version of this control is downloadable from site http://sourcegrid.codeplex.com/.

In this article I will want to supply a panoramic on the utilization and on the functionality of the control SourceGrid, for the details on the classes, properties or methods you can consult the documentation in CHM format or the example project in the ZIP file.

Use SourceGrid

In the assembly SourceGrid2.dll are present 2 controls 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 fundamental distinctions to do: virtual cells and real cells. Virtual cells are the cells that determine the appearance and the behavior of the cell but don't contain the value, the real cells have the same properties of the virtual cells but contain also the value of the cell, they are therefore associated to a specific position of the grid.

Every cells are composed by three fundamental parts:

  • DataModel : The DataModel is the class that manages the value of the cells. Converts the value of the cell in 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 a great flexibility and reusability of code, save time and supplies a solid base for every type of customizations. For the more common cases there are some classes already arranged and configured, but with little 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 with not many cells. In fact in this control every cells are 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.

After to have inserted the control in the form we can begin to write our code to use the grid in the Load event of the form like this:

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 positions with a cell. I have used the SourceGrid2.Cells.Real namespace where are present all the real cells.

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

SourceGrid2.Cells.Real.Cell l_Cell = new SourceGrid2.Cells.Real.Cell(
  "Custom back color");
l_Cell.BackColor = Color.LightGreen;
grid1[0,0] = l_Cell;

These are the main visual properties of a cell (for an entire list to 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 editor 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 cycle for I have then created the other cells using for each column a specific type. The Cell class creates automatically 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 on the cell. The form should look equal to the one in the following figure, this example is present also in the project SampleProject with the ZIP file.

GridVirtual

The GridVirtual control is the ideal when is necessary to visualize a lot of cells and you have already available a structure 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 order automatically any external data structure without copying its content) and the feature of RowSpan and ColumnSpan that allow to span a cell across other adjacent cells. Another disadvantage is that to create a virtual grid is a little difficult.

The main concept in a virtual grid is that the cells do not contain the values, but read and write the value in 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 the GridVirtual is therefore necessary to create a class that derives from CellVirtual and to personalize the reading using the data source chosen. Usual is better to create also a control that derive from GridVirtual, to have a greater flexibility and a more solid code, overriding the method GetCell. If you prefer you can directly use the GridVirtual control and the event GettingCell. The purpose of the method GetCell and of the event GettingCell is to return, for a given position (row and column), the chosen cell. This allows a large flexibility because you can return for a specific type any ICellVirtual, for example you could return a cell of type header when the row is 0.

In the following example I create a virtual grid that reads and writes the values in an array. First I have inserted the control GridVirtual in a form, then I write this code that defines our virtual class:

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));
  }
}

With 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 have 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, then I have created a new instance of the previous cell and with the method BindToGrid I have linked the cell to the grid. I have created a single cell that will be used for every position of the matrix. 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 declare 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 equal to the one in the following picture, this example is present also in the project SampleProject included in the ZIP file.

VisualModel

Namespace: SourceGrid2.VisualModels

Every cell have 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 to sharing the same visual model between more cells. In fact the same instance of VisualModel can be used on more cells simultaneously 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 contains 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 to drawing more then one image in the cell.

*The VisualModel marked with an asterisk require 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 default read-only instances easily useable:

  • 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 calls automatically the BackColor property of the VisualModel associated. To facilitate the utilization of the more common properties if you write Cell.BackColor = Color.Black; the cell automatically clone 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, is necessary to populate the property DataModel of the cell. If this property is null is not possible to change the value of the cell and the string conversion will be a simple ToString of the value.

Usual a DataModel use a TypeConverter of the type asked 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 only by code, 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 use a read-only editor because the value is changed clicking directly on the checkbox).
  • EditorControlBase - Abstract Class that help 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 mask.
  • EditorUITypeEditor - Supplies the editing of the cell of all types that have an UITypeEditor. A lot of types support this interface: DateTime, Font, a lot of enum, ...

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

To create an editable cell there are 2 possibilities:

  • Create the cell specifying the value type. In this manner the cell calls automatically an utility function, Utility.CreateDataModel, that returns a DataModel in base to the type specified.
    grid1[0,0] = new SourceGrid2.Cells.Real.Cell("Hello", 
      typeof(string));
  • Create separately the DataModel 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 more of cells.

If you need a greater control on the type of editor or there are special requirements is possible to create manually the editor class. In this case for example I create manually the class EditorTextBox and then I call the property MaxLength and CharacterCasing.

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

Some properties are defined to a DataModel level, while other to an editor control level, in this case the property CharacterCasing is defined to a TextBox control level. To use these properties is necessary therefore to force a linking of the editor to the grid with the method AttachEditorControl and then call the method GetEditorTextBox to returns the instance of the TextBox. This mechanism is also useful for create 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 is possible to create custom DataModel editor with custom control or special behaviors. In the following picture it is possible to observe most of the editors available and some options like image properties:

BehaviorModel

Namespace: SourceGrid2.BehaviorModels

Every cell have a collection of BehaviorModel 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 more cells and allows a great flexibility and simplicity of 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.BehaviorModel