Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

SourceGrid - Open Source C# Grid Control

0.00/5 (No votes)
4 Aug 2013 84  
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.BehaviorModels.CheckBox* - Behavior of a CheckBox. (need ICellCheckBox)
  • SourceGrid2.BehaviorModels.Cursor* - Allows to link 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* - Allows to show a ToolTipText linked to a cell. (need ICellToolTipText)
  • SourceGrid2.BehaviorModels.Unselectable - Blocks a cell to receive the focus.
  • SourceGrid2.BehaviorModels.ContextMenu* - Allows to show a contextmenu linked to a cell. (need a ICellContextMenu)
  • SourceGrid2.BehaviorModels.CustomEvents - Expose a list of events that you can use without deriving from a BehaviorModel.
  • SourceGrid2.BehaviorModels.BindProperty - Allows to link the value of a cell to an external property.
  • SourceGrid2.BehaviorModels.BehaviorModelGroup - Allows to create a BehaviorModel that automatically calls a list of BehaviorModel, useful when a behavior 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 of a cell that 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.SortableHeader
  • SourceGrid2.BehaviorModels.ColumnHeader.NotSortableHeader
  • 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 change 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 in Load event of a form this code:

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 implementation, 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 each other implementation, 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 these classes we can see that these classes use the previous models according to the role of the cell. There are however models that require special interfaces and in this case the cells implement 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 e BehaviorModels.CheckBox.Default 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 these informations SourceGrid supplies 2 properties:

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

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

There are many ways to manipulate 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 manage correctly scrollbars, columns and rows fixed and a lot other details, the grid inside has a panels 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 not fixed cells.
  • 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 first fired to the panels and then automatically moved to GridVirtual and 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 or otherwise you can connect a ContextMenu directly to a specific cell. Look at the example 10 in the project SampleProject for further details.

Other Informations

Focus and Selection

A cell can be selected of can have the focus. Only one cell can have the focus, identified by the FocusCellPosition property of the grid, instead 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 the copy/paste.

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 is necessary to visualize a lot of cells and try always to share the models (DataModel, VisualModel, BehaviorModel) between more possible cells. The performance of the grid is quite good even if the drawing code can be still optimized, especially when scrolling. It is possible to consult the project SampleProject for further information on the performance of the grid.

Extensions

In the project SampleProject are present a lot of examples and parts of code that can give ideas or suggestions of how implements custom grid, particularly in the folder Extensions are present some grids that supply functionality like the binding to a DataSet (DataTable), to an Array and to an ArrayList.

Screenshots

How To

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

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.
  • Supports natively all of the types of data that have a TypeConverter or an UITypeEditor associated.
  • Any .NET control can be used like 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 automatic based to the content of the cells.
  • Supports features of RowSpan and ColumnSpan, to unite more cells.
  • Supports automatic operations of Copy and Paste.
  • Supports natively 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 to binding any type of data source.

... and what cannot do

  • SourceGrid doesn't have a designer, all should be done with code.
  • No printing support.

Change SourceGrid code

It is 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 to change the file SourceGrid2.snk with a personalized version to not have problems of 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.
  • Support for Masked Edit textbox.

Known problems

  • There is not Cut support.
  • The editor NumericUpDown is not aligned correctly to the cell.
  • The Shift key does not work with the arrows keys and there are still some problems with header cells.

Previous versions

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

  • The major features of the grid work with the ICellVirtual interface, no more with Cell class. This interface contains only necessary methods and therefore is poorer. For this the code that before was:
    grid[0,0] = new SourceGrid.Cell("Ciao");
    grid[0,0].BackColor = Color.White;
    now should be transformed in
    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 identified from the Cell class while now is the interface ICellVirtual and the major change of this class is that does not contain informations about the position (row and column).
  • A lot of the methods of the grid that before used the Cell type now use the Position type, is however possible to extract the interface ICellVirtual (and then cast to more specific interfaces) given a struct Position with the method Grid.GetCell
  • Now the grid support natively the property BorderStyle that is able therefore to eliminate the eventual Panel that before was necessary to introduce a border.
  • All of the code that first was bound to the events of a cell now must be moved in a BehaviorModel, you can use for example the SourceGrid2.BehaviorModels.CustomEvents.
  • The Selection object is no more a collection of cells but a collection of Range.
  • With the insertion of the Rows and Columns objects the code that first should work on lines and columns now results simpler, besides a lot of methods that before were in the grid now are in the RowInfoCollection, RowInfo, ColumnInfoCollection or ColumnInfo classes.
  • The object CellsContainer is no more present, and even if logically was replaced from the panels, the more commons are linked directly to the grid and therefore the code that before used CellsContainer now could use directly the Grid control.
  • The old object ICellModel now is the object IDataModel, while the object VisualProperties now became IVisualModel.
  • The class CellControl for now is no more supported, I will think in future if introduce it again.

History

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here