SourceGrid

Lib Version: 4.21
Doc Version: 2.2

Overview



Introduction

SourceGrid is a .NET Windows Forms grid control written entirely in C# with managed code. SourceGrid can be used to visualize or to change data in a table format.
SourceGrid con be used bound to a data source (typically a DataView) or creating each cell directly.
There are a lot of controls of this type available, but often are expensive, difficult to customize or too DataSet oriented.
SourceGrid use only managed code (without API or Interop) and can be used with any .NET 2 compatible environments.

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.
For more information, a discussion forums, bug tracker system or to download the latest release go to the CodePlex page: http://sourcegrid.codeplex.com/ or to my home page http://www.devage.com/. Previous versions can be found at SourceForge page: http://sourceforge.net/projects/sourcegrid.

Quick Start

Installation

To use SourceGrid you must have a .NET 2 compatible development environment (like Visual Studio 2005).
Download the latest release from http://www.codeplex.com/sourcegrid/. Unzip the file and reference from your project these assemblies:

Typically I suggest to always copy in the same location the *.xml files that you can find in the same directory of the assemblies to use the IDE IntelliSense features.

Open the form where you want to add the grid control, open the IDE ToolBox, right-click and select "Choose Items...". Browse and add SourceGrid.dll and SourceGrid.Extensions.dll assemblies on the IDE ToolBox.

These assemblies are the same assemblies required by the runtime that you must redistribute with your application to the end-user.

SourceGrid controls

There are 2 main controls inside the SourceGrid.dll assembly:

There are therefore two fundamentally distinct objects: virtual cells and real cells. Virtual cells are cells that determine the appearance and the behaviour 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.

You can use the Grid control for any kinds of grid where you don't need to display large amounts of cells (typically less than 50.000 cells). If you have to display large amount of cells you must usually use a GridVirtual derived controls, see the next chapters for more information.
Typically in this article I will use the Grid control because is more simply to use especially for simply examples. But consider that basically the same code can be used also for GridVirtual.
The Grid is also used if you have unusual grids that require maximum flexibility.

Drag the Grid control inside your forms as any other .NET control and start using it.

Basic examples

For now SourceGrid has a poor design time support, so usually you must write manually the code to manipulate the grid.
Suppose that you have a Grid control named grid1 you can write this code in the Form.Load event:

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

grid1.AutoSizeCells();
    

As you can see you can use the grid like a 2 dimension array. 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. Each kind of cell defines its own visual aspect and behaviour.
The form should look like the one in the following picture.

FirstGrid

The grid created supports sorting, column sizing and editing of the cells.

Here some other important features:

If you want to change some visual properties of the cell you must use the View class.
Let's see another example:

grid1.BorderStyle = BorderStyle.FixedSingle;

grid1.ColumnsCount = 3;
grid1.FixedRows = 1;
grid1.Rows.Insert(0);

SourceGrid.Cells.Views.ColumnHeader boldHeader = new SourceGrid.Cells.Views.ColumnHeader();
boldHeader.Font = new Font(grid1.Font, FontStyle.Bold | FontStyle.Underline);

SourceGrid.Cells.Views.Cell yellowView = new SourceGrid.Cells.Views.Cell();
yellowView.BackColor = Color.Yellow;
SourceGrid.Cells.Views.CheckBox yellowViewCheck = new SourceGrid.Cells.Views.CheckBox();
yellowViewCheck.BackColor = Color.Yellow;


grid1[0, 0] = new SourceGrid.Cells.ColumnHeader("String");
grid1[0, 0].View = boldHeader;

grid1[0, 1] = new SourceGrid.Cells.ColumnHeader("DateTime");
grid1[0, 1].View = boldHeader;

grid1[0, 2] = new SourceGrid.Cells.ColumnHeader("CheckBox");
grid1[0, 2].View = boldHeader;
for (int r = 1; r < 10; r++)
{
    grid1.Rows.Insert(r);

    grid1[r, 0] = new SourceGrid.Cells.Cell("Hello " + r.ToString(), typeof(string));
    grid1[r, 0].View = yellowView;

    grid1[r, 1] = new SourceGrid.Cells.Cell(DateTime.Today, typeof(DateTime));
    grid1[r, 1].View = yellowView;

    grid1[r, 2] = new SourceGrid.Cells.CheckBox(null, true);
    grid1[r, 2].View = yellowViewCheck;
}
    

I have created a column header view with a style FontStyle.Bold | FontStyle.Underline, one view for a standard cell with a yellow backcolor and a checkbox view always with a yellow backcolor. Then I have assigned these instances to the View property of each cell.
The form should look like the one in the following picture.

FirstGrid2

You can note that I have assigned the same instance of the View class for many cells. This is useful to optimize the resources used.

Each cell can have an editor (Editor property) associated. The editor is used to edit the cell value. You can manually create an editor class (see SourceGrid.Cells.Editors namespace) or use the SourceGrid.Cells.Editors.Factory class to create an editor based on a Type. You can also use the Cell constructor that automatically calls SourceGrid.Cells.Editors.Factory if you specify the Type parameter.
Here an example that creates some cells and the editors associated using one of the methods described.

//A DateTime editor
grid1[r, c] = new SourceGrid.Cells.Cell(DateTime.Today, typeof(DateTime));

//A string editor
grid1[r, c] = new SourceGrid.Cells.Cell("Ciao", typeof(string));

//A double editor
grid1[r, c] = new SourceGrid.Cells.Cell(58.4);
grid1[r, c].Editor = SourceGrid.Cells.Editors.Factory.Create(typeof(double));
    

Like the View classes also the editors can be shared between one or more cells.

Now you can start to work with SourceGrid, for more advanced information see the chapters below.

Basic Concepts

Grid Control

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

Using the control in a Windows form is trivial. It's just like adding any other control like a Button or a DataGrid. First, create or open a Windows Application project and open a Windows Form into the designer. Now you are ready to customize the Toolbox: Right-Click the Toolbox, .NET Framework Components, Browse, select the DevAge.SourceGrid.dll. The Grid Control is now added to the Toolbox and can be inserted in Windows Form as any other control.

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 SourceGrid.Cells.Cell("Hello from Cell 0,0");
grid1[1,0] = new SourceGrid.Cells.Cell("Hello from Cell 1,0");
grid1[0,1] = new SourceGrid.Cells.Cell("Hello from Cell 0,1");
grid1[1,1] = new SourceGrid.Cells.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 SourceGrid.Cells namespace which contains the definition for real cells.

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

See the other chapters for more information.

GridVirtual Control

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 each cell reads and writes its value from an external data structure and the grid doesn't maintain all the rows and columns but are normally read directly from the data source. This idea was implemented with an abstract GridVirtual class and with the abstract methods CreateRowsObject, CreateColumnsObject and GetCell. Then you can use a special IValueModel to read the values directly from the data source.
To use GridVirtual it is therefore necessary to create a class that derives from GridVirtual and personalize the reading of the data source overriding CreateRowsObject, CreateColumnsObject and GetCell methods.
The purpose of the method GetCell is to return, for a given position (row and column), the chosen cell, the methods CreateRowsObject and CreateColumnsObject are used to create the columns and the rows objects for the used data source. 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.

Usually you don't need to use directly the GridVirtual but one of the derived controls. For now I have implemented two controls that directly use the virtual grid features:

If you want to create your custom control to read from a specific data source you can see ArrayGrid class for an example.

Cell overview

Every cell is composed of four fundamental parts based on a modified Model-View-Controller pattern:

SourceGridMVC

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

Rows and Columns

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

When using a real grid the base classes are extended with the RowInfoCollection and the ColumnInfoCollection. These are collection of RowInfo and ColumnInfo classes. When using a virtual grid you must extend the base classes with your custom code to provide information of your data source.

Manipulating Rows and Columns on real grid

NOTE: Only valid for real grid.

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:

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.

After allocating the rows and columns, you must create for each position of the grid the required cells, like this code:

grid1.Redim(2,2);
grid1[0, 0] = new SourceGrid.Cells.Cell("Cell 0, 0");
grid1[1, 0] = new SourceGrid.Cells.Cell("Cell 1, 0");
grid1[0, 1] = new SourceGrid.Cells.Cell("Cell 0, 1");
grid1[1, 1] = new SourceGrid.Cells.Cell("Cell 1, 1");

Model

Namespace: SourceGrid.Cells.Models

The purpose of the Model classes is to separate the data in the cells from the cell object. This separation is used for two main reasons:

Every cell has a property Model that gets or sets a ModelContainer object. This class is a collection of IModel interface. Each IModel interface contains all the properties used for a specific feature.

The main Model is the ValueModel which contains the value of the cell, but there also other models and you can crate your custom models. Here are the default models:

Each of these models contains the properties specified for each features, for example the IToolTipText contains the ToolTipText string.

Each cell has a collection of models to allow using many features on a single cell.

Usually using real cell you can use a model that simply implement the right interface storing directly the required data; using virtual grid you can implement the interface to bind the values directly to an external data source.

View

Namespace: SourceGrid.Cells.Views

Every cell has a property View to gets or sets an interface of type IView. The cell uses this interface to draw and to customize the visual properties of the cell.

The purpose of the View is to separate the drawing code from the rest of the code and allows sharing of the same visual properties between cells. In fact the same instance of View can be used on many cells simultaneously thus optimizing the use of the resources of the system.

These are the default View classes in the namespace SourceGrid.Cells.Views:

*The View marked with an asterisk requires special components to work correctly, for example the CheckBox view needs an ICheckBox Model.

Some of the previous classes contain one or more static properties with some convenient default instances.

This code shows how to create a View class, change some properties and assign it to more cells previously created:

SourceGrid.Cells.Views.Cell view = new SourceGrid.Cells.Views.Cell();
view.BackColor = Color.Khaki;
grid1[0,0].View = view;
grid1[2,0].View = view;

With some line of code you can create your custom View, in this case I suggest deriving your class from Cell, which already has implemented some default methods. In the following code example I create a View that draws a red ellipse over the cell:

public class MyView : SourceGrid.Cells.Views.Cell
{
        protected override void DrawCell_Background(SourceGrid.Cells.ICellVirtual p_Cell,
         SourceGrid.Position p_CellPosition, PaintEventArgs e, Rectangle p_ClientRectangle)
        {
                base.DrawCell_Background (p_Cell, p_CellPosition, e, p_ClientRectangle);

                e.Graphics.DrawEllipse(Pens.Red, p_ClientRectangle);
        }
}

To use this new View in a cell use this code:

MyView myView = new MyView();
//...... code to populate the grid
grid1[r, c].View = myView;

Controller

Namespace: SourceGrid.Cells.Controllers

Every cell has a property Controller that gets or sets a ControllerContainer object. This class is a collection of IController interface. Each IController interface can be used to extend the behaviour of the cell using many events like Click, MouseDown, KeyPress, ...

These are the default classes:

*The Controller marked with an asterisk need special models to complete their tasks.

Some of the previous classes contain one or more static properties with some convenient default instances.
Here the list of events that can be used inside a Controller:

With some line of code you can create your custom Controller, in this case I suggest deriving your class from ControllerBase, which already have implemented some default methods. In the following code example I create a Controller that changes the backcolor of the cell when the user moves the mouse over it:

public class MyController : SourceGrid.Cells.Controllers.ControllerBase
{
        private SourceGrid.Cells.Views.Cell MouseEnterView = new SourceGrid.Cells.Views.Cell();
        private SourceGrid.Cells.Views.Cell MouseLeaveView = new SourceGrid.Cells.Views.Cell();
        public MyController()
        {
                MouseEnterView.BackColor = Color.Green;
        }

        public override void OnMouseEnter(SourceGrid.CellContext sender, EventArgs e)
        {
                base.OnMouseEnter (sender, e);

                sender.Cell.View = MouseEnterView;

                sender.Grid.InvalidateCell(sender.Position);
        }
        public override void OnMouseLeave(SourceGrid.CellContext sender, EventArgs e)
        {
                base.OnMouseLeave (sender, e);

                sender.Cell.View = MouseLeaveView;
                
                sender.Grid.InvalidateCell(sender.Position);
        }
}

To use this new Controller in a cell use this code:

MyController myController = new MyController();
//...... code to populate the grid
grid1[r, c].AddController(myController);

You can also add a controller to the whole grid to apply the same controller to all the cells:

grid1.Controller.AddController(new MyController());

Consider for example the Controller below that open a messagebox each time a user click on a cell:

class ClickController : SourceGrid.Cells.Controllers.ControllerBase
{
    public override void OnClick(SourceGrid.CellContext sender, EventArgs e)
    {
        base.OnClick(sender, e);

        object val = sender.Value;
        if (val != null)
            MessageBox.Show(sender.Grid, val.ToString());
    }
}        
        

You can add this controller to all the cells with this code:

grid1.Controller.AddController(new ClickController());

Here another example of how to use a controller to check when the value of a cell change, using the OnValueChanged event:

public class ValueChangedEvent : SourceGrid.Cells.Controllers.ControllerBase
{
    public override void OnValueChanged(SourceGrid.CellContext sender, EventArgs e)
    {
        base.OnValueChanged(sender, e);

        string val = "Value of cell {0} is '{1}'";

        MessageBox.Show(sender.Grid, string.Format(val, sender.Position, sender.Value));
    }
}        
        

You can add this controller to all the cells with this code:

grid1.Controller.AddController(new ValueChangedEvent());

Each time the value of a cell change the previous controller show a messagebox with the position of the cell and the new value.

Editor

Namespace: SourceGrid.Cells.Editors

Every cell has a property Editor that gets or sets an EditorBase object. This class is used to supply a cell editor. If this property is null it is not possible to edit the cell.

Usually the Editor uses the Model class to manage the necessary conversion, particularly the string conversion (used to represent the cell value).

These are the default classes:

An Editor can be shared between many cells of the same grid; for example you can use the same Editor for every cell of a column, but always with the same grid.

Each Editor class has a property Control that returns an instance of the Windows Forms control used to edit the cell. You can use this instance to customize the editor control or for advanced features.

Here some ways to create an editable cell:

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

//String editor
SourceGrid.Cells.Editors.TextBox editorString = new SourceGrid.Cells.Editors.TextBox(typeof(string));
editorString.Control.MaxLength = 10;
//String cell
grid1[0, 0] = new SourceGrid.Cells.Cell("Hello");
grid1[0, 0].Editor = editorString;

The editor can be used also to customize the format of the cell, in the code below for example I create a cell with a custom numeric format:

//Double editor
SourceGrid.Cells.Editors.TextBoxNumeric editorDouble = new SourceGrid.Cells.Editors.TextBoxNumeric(typeof(double));
editorDouble.TypeConverter = new DevAge.ComponentModel.Converter.NumberTypeConverter(typeof(double), "#,###.00");
//String cell
grid1[0, 0] = new SourceGrid.Cells.Cell(9419.3894);
grid1[0, 0].Editor = editorDouble;

I have used the TypeConverter property to customize the conversion to and from string values. There are many other TypeConverter available:

Below another example that create a DateTime editor with a custom format:

//DateTime editor with custom format
string[] dtParseFormats = new string[] { dtFormat2 };
System.Globalization.DateTimeStyles dtStyles = System.Globalization.DateTimeStyles.AllowInnerWhite |
                 System.Globalization.DateTimeStyles.AllowLeadingWhite | 
                 System.Globalization.DateTimeStyles.AllowTrailingWhite | 
                 System.Globalization.DateTimeStyles.AllowWhiteSpaces;
TypeConverter dtConverter = new DevAge.ComponentModel.Converter.DateTimeTypeConverter(dtFormat2, dtParseFormats, dtStyles);
SourceGrid.Cells.Editors.TextBoxUITypeEditor editorDt2 = new SourceGrid.Cells.Editors.TextBoxUITypeEditor(typeof(DateTime));
editorDt2.TypeConverter = dtConverter;

grid[currentRow, 1] = new SourceGrid.Cells.Cell(DateTime.Today);
grid[currentRow, 1].Editor = editorDt2;
        

The following picture shows most of the editors available and some special cells (picture from Sample 3):

Editors

It is possible to create a custom Editor with custom control or special behaviours with few lines of code.
You can derive your custom class from EditorControlBase and create any Windows Forms control. This is an example of an editor that uses DateTimePicker control:

public class DateTimePicker : EditorControlBase
{
        public DateTimePicker():base(typeof(System.DateTime))
        {
        }
        protected override Control CreateControl()
        {
                System.Windows.Forms.DateTimePicker dtPicker = 
                    new System.Windows.Forms.DateTimePicker();
                dtPicker.Format = DateTimePickerFormat.Short;
                dtPicker.ShowCheckBox = AllowNull;
                return dtPicker;
        }
        protected override void OnChanged(EventArgs e)
        {
            base.OnChanged(e);
            if (Control != null)
                Control.ShowCheckBox = AllowNull;
        }
        public new System.Windows.Forms.DateTimePicker Control
        {
                get
                {
                        return (System.Windows.Forms.DateTimePicker)base.Control;
                }
        }
        protected override void OnStartingEdit(CellContext cellContext, 
                    Control editorControl)
        {
                base.OnStartingEdit(cellContext, editorControl);
                System.Windows.Forms.DateTimePicker dtPicker = 
                    (System.Windows.Forms.DateTimePicker)editorControl;
                dtPicker.Font = cellContext.Cell.View.Font;
        }
        public override void SetEditValue(object editValue)
        {
                if (editValue is DateTime)
                        Control.Value = (DateTime)editValue;
                else if (editValue == null)
                        Control.Checked = false;
                else
                        throw new SourceGridException
                            ("Invalid edit value, expected DateTime");
        }
        public override object GetEditedValue()
        {
            if (Control.Checked)
                return Control.Value;
            else        
                return null;
        }
        protected override void OnSendCharToEditor(char key)
        {
        }
}

Consider that you can share the same instance of the editor with many cells but only with a single grid control. Basically each editor is associated with only one grid.
For large grid it is always a good idea to share the editors, because each editor has a Windows Forms Control associated and so it is quite heavy.

Advanced cells

Namespace: SourceGrid.Cells

These are the default cells available:

The goal of these classes is to simplify the use of View, Model, Controller and Editor classes. If we look at the code of any of the cell classes we can see that these classes use these components according to the role of the cell.
This is, for example, the code of the cell SourceGrid.Cells.CheckBox:

public class CheckBox : Cell
{
        public CheckBox(string caption, bool checkValue):base(checkValue)
        {
                if (caption != null && caption.Length > 0)
                        View = Views.CheckBox.MiddleLeftAlign;
                else
                        View = Views.CheckBox.Default;

                Model.AddModel(new Models.CheckBox());
                AddController(Controllers.CheckBox.Default);
                AddController(Controllers.MouseInvalidate.Default);
                Editor = new Editors.EditorBase(typeof(bool));
                Caption = caption;
        }
        private Models.CheckBox CheckBoxModel
        {
                get{return (Models.CheckBox)Model.FindModel(typeof(Models.CheckBox));}
        }
        public bool Checked
        {
                get{return CheckBoxModel.GetCheckBoxStatus(this, Range.Start).Checked;}
                set{CheckBoxModel.SetCheckedValue(this, Range.Start, value);}
        }
        public string Caption
        {
                get{return CheckBoxModel.Caption;}
                set{CheckBoxModel.Caption = value;}
        }
}

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.ActivePosition 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 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.

Use the Grid.Selection.SelectCell method or SelectRange method to add or remove specific cells from the selection.

To list all the selected cells you can use Grid.Selection.GetRanges() method that returns a list of selected Range, or check if a particular cell is selected using the IsSelectedCell method. You can customize many aspects of the selection with these properties: Grid.Selection.BackColor, Grid.Selection.Border, Grid.Selection.FocusBackColor, ...

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

You can set the selection mode using the Grid.SelectionMode property. The available options are: GridSelectionMode.Cell, GridSelectionMode.Row and GridSelectionMode.Column. In this way you can set the grid to select only entire row, entire column or only single cell.

To enable or disable multi selection you must use the Grid.Selection.EnableMultiSelection property. You can select multiple cells using the mouse or with the Ctrl or Shift key.

Position and Range

Two of the most used objects in the project are the structures 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.

You can read the actual location of a specified cell using grid.PositionToRectangle method. The resulting rectangle is relative to the grid client area. You can convert the resulting Rectangle to an absolute screen position using grid.PointToScreen.

You can obtain the Position for a specific client area Point using grid.PositionAtPoint method.

CellContext

CellContext is a structure that encapsulates a Cell and a Position and contains all the method to manipulate the cells.
The most important methods are:

Here a common example that show how to use a CellContext class:

SourceGrid.CellContext context = new SourceGrid.CellContext(grid, new SourceGrid.Position(r, c));
context.Value = "hello";
context.StartEdit();
        

Usually a CellContext instance is automatically created as a parameter of the controller events, in this way you can always access the main cell properties.

Advanced Features

Border

Each View class has a property Border of type DevAge.Drawing.IBorder. The DevAge.Drawing.IBorder is a generic interface that can be used to draw a border around the cell.

Usually the IBorder interface it is implemented by the DevAge.Drawing.RectangleBorder structure.

Below an example to change a border of a cell:

DevAge.Drawing.Border border = new DevAge.Drawing.Border(Color.Red, 1);
DevAge.Drawing.RectangleBorder cellBorder = new DevAge.Drawing.RectangleBorder(border, border);

SourceGrid.Cells.Views.Cell view = new SourceGrid.Cells.Views.Cell();
view.Border = cellBorder;

grid[r, c].View = view;
        

The default border set only the Right and Bottom side, in this way when you have a group of cells you don't see a double border.

You can also set the border for the grid using the Grid.BorderStyle property.

See form sample 26 for more information.

ToolTip

You can bind a ToolTip on each cell. You must create a SourceGrid.Cells.Controllers.ToolTipText controller and associate it to a cell. Here an example:

SourceGrid.Cells.Controllers.ToolTipText toolTipController = new SourceGrid.Cells.Controllers.ToolTipText();
toolTipController.ToolTipTitle = "ToolTip example";
toolTipController.ToolTipIcon = ToolTipIcon.Info;
toolTipController.IsBalloon = true;

grid1[r, c] = new SourceGrid.Cells.Cell("Hello");
grid1[r, c].ToolTipText = "Example of tooltip, bla bla bla ....";
grid1[r, c].AddController(toolTipController);
        

The ToolTipText property of the cell automatically populates a SourceGrid.Cells.Models.IToolTipText interface bound to the standard cell.

ToolTip

See form sample 26 for more information.

Note: To use the ToolTip on a GridVirtual you must define your custom SourceGrid.Cells.Models.IToolTipText implementation that read the value from your data source and then add the controller and the model to the virtual cell. See form sample 41.

ContextMenu

You can create a ContextMenu (PopUp Menu) for a cell using the code below. First define a controller with the ContextMenu:

//Define a controller with a ContextMenu
public class PopupMenu : SourceGrid.Cells.Controllers.ControllerBase
{
        ContextMenu menu = new ContextMenu();
        public PopupMenu()
        {
        menu.MenuItems.Add("Menu 1", new EventHandler(Menu1_Click));
                menu.MenuItems.Add("Menu 2", new EventHandler(Menu2_Click));
        }

        public override void OnMouseUp(SourceGrid.CellContext sender, MouseEventArgs e)
        {
                base.OnMouseUp (sender, e);

                if (e.Button == MouseButtons.Right)
                        menu.Show(sender.Grid, new Point(e.X, e.Y));
        }

    private void Menu1_Click(object sender, EventArgs e)
    {
        //TODO Your code here
    }
    private void Menu2_Click(object sender, EventArgs e)
    {
        //TODO Your code here
    }
}
        

Then add the controller to a cell:

PopupMenu menuController = new PopupMenu();
...
grid1[r, c] = new SourceGrid.Cells.Cell("Hello");
grid1[r, c].AddController(menuController);
        

See form sample 26 for more information.

Clipboard

SourceGrid supports clipboard copy, paste and cut operations. To enable these features you can use this code:

grid1.ClipboardMode = SourceGrid.ClipboardMode.All;
        

The user can then copy, paste and cut the selected cells using Ctrl+C, Ctrl+V or Ctrl+X or delete the content using Delete key.

Drag and Drop

NOT SUPPORTED: From version 4.5 drag and drop is no more supported, I will try to insert this feature again soon.

Suggestions

Usually with SourceGrid you must do all the work with code, for this reason one import suggestion is to organize it in a good and reusable manner. Here some tips:

If you need to customize some aspects of the cell I usually suggest to create a new class and write your customization code here. In this manner you can reuse your class in all your application and forms. Suppose for example that you want to create all the cells with a gray back color, you can write these new classes:

public class GrayView : SourceGrid.Cells.Views.Cell
{
        public new static readonly GrayView Default = new GrayView();
        public GrayView()
        {
                BackColor = Color.LightGray;
        }
}

public class GrayCell : SourceGrid.Cells.Cell
{
        public GrayCell(object val):base(val)
        {
                View = GrayView.Default;
        }
}

This rule can be applied to any aspects of the grid, for example you can create a new Controller class and use it in your cell.

Another important feature that we can see in the previous code is that I have assigned the View property from a static variable. In this way you can share the same instance with many cells, and optimize the resources of the system.

Another little suggestion is about the use of namespaces. Many SourceGrid classes have the same name of some system classes. For example there is a CheckBox cell with the same name of the CheckBox control of System.Windows.Forms, for this reason is sometime difficult to use a normal using statement. If you don't like to use a long namespace and want a more readable code I suggest to rename the namespace in the using statement. For example you can replace this code:

SourceGrid.Cells.Button bt = new SourceGrid.Cells.Button();

with:

using Cells = SourceGrid.Cells;
.................
Cells.Button bt = new Cells.Button();

Extensions

Other then the default Grid control there are some extensions that can be used in some specific cases. You can directly use one of these extensions or you can copy the code and create your own extension.

DataGrid

SourceGrid.DataGrid is control derived from GridVirtual used for binding the data to a DevAge.ComponentModel.BoundListBase class.
The BoundListBase is an abstract class that can be used to as a generic layer to binding any kind of list control to a list data source.
Typically a data source is a System.Data.DataView or a custom list class. Currently there are 2 concrete classes that use BoundListBase:

Basically the DataGrid control uses a special Model class of type IValueModel that reads the data directly from the data source. SourceGrid.DataGrid has a property DataSource used to store the BoundListBase object. Here is a simple example on how to use this control:

//Create a sample DataTable
DataTable table = new DataTable();
table.Columns.Add("A", typeof(string));
table.Columns.Add("B", typeof(bool));
table.Rows.Add(new object[]{"Row 1", false});
table.Rows.Add(new object[]{"Row 2", true});
table.Rows.Add(new object[]{"Row 3", false});

dataGrid1.DataSource = new DevAge.ComponentModel.BoundDataView(table.DefaultView);

In the previous code I have created a DataTable with 2 columns and some rows, then you can use the DefaultView property to retrieve a DataView class and assign it to the DataGrid control by creating an instance of a BoundDataView class. If you want you can customize the columns using the Columns property. This property returns a collection of DataGridColumn objects, you can customize each column to create customized cells. Here is an example:

//Create a custom View class
SourceGrid.Cells.Views.Cell view = new SourceGrid.Cells.Views.Cell();
view.BackColor = Color.LightBlue;
//Manually add the column
SourceGrid.DataGridColumn gridColumn;
gridColumn = dataGrid.Columns.Add("Country", "Country", typeof(string));
gridColumn.DataCell.View = view;

In this example I have manually created a column and assigned a LightBlue back color at the cell.

You can also use the DataGridColumn.Conditions to dynamically change the data cell. For example in the following code I create a condition to use a different cell backcolor for even rows (alternate backcolor):

SourceGrid.Conditions.ICondition condition = 
    SourceGrid.Conditions.ConditionBuilder.AlternateView(gridColumn.DataCell.View, Color.LightGray, Color.Black);
gridColumn.Conditions.Add(condition);

In the following other example I create condition to use a View with a bold font and a green forecolor when a specific column is true.

SourceGrid.Conditions.ConditionView selectedConditionBold = 
        new SourceGrid.Conditions.ConditionView(viewSelected);
selectedConditionBold.EvaluateFunction = delegate(SourceGrid.DataGridColumn column, 
                                                    int gridRow, object itemRow)
                {
                    DataRowView row = (DataRowView)itemRow;
                    return row["Selected"] is bool && (bool)row["Selected"] == true;
                };
gridColumn.Conditions.Add(selectedConditionBold);

Currently there are 2 types of conditions: SourceGrid.Conditions.ConditionView that can be used to use a special view and SourceGrid.Conditions.ConditionCell to use a completely different cell. Both conditions can be used with a delegate to evaluate the row using your specific code.

In the sample project you can find more examples and code.

ArrayGrid

SourceGrid.ArrayGrid is a control derived from GridVirtual used to bind the grid to an array. You can use this control setting the DataSource property with any System.Array. You can then customize the cells using the ValueCell, Header, RowHeader and ColumnHeader properties.

PlanningGrid

SourceGrid.Planning.PlanningGrid control is a UserControl that internally use a real Grid to create a grid that can be used to show appointments. You can call the LoadPlanning method to set the range of the visible days and hours, and then add the appointments using the Appointments properties. This is a collection of IAppointment interface (implemented by the AppointmentBase class).

Each appointment has a View property that can be used to customize the appointment cell.

References

DevAgeSourcePack references these assemblies:

Remember to redistribute these assemblies with SourceGrid.dll.

Future developments

VB.NET

As any .NET control you can use SourceGrid with any .NET language, also VB.NET. Unfortunately I don't have any example for VB.NET, consider anyway that the code is very similar to C#. Here some of the more common differences:

C#

VB.NET

double x;

Dim x as Double

typeof(string)

GetType(String)

Class.Event += new EventHandler(Class_Event);

AddHandler Class.Event, Addressof Class_Event

grid[r,c]

grid.Item(r,c)

public class Dog
{
    public Dog()
    {
    }
    public string Name
    {
        get{return mName;}
        set{mName = value;}
    }
}
                    
Public class Dog
    Public sub New()
    
    End sub
    
    Public Property Name() as String
        Get
            Return mName
        End Get
        Set
            mName = Value
        End Set
    End Property
End class
                    

List<string> x;

Dim x as List(Of String)

FAQ

Frequently Asked Questions:

Q: Can I use this project with Visual Studio 2005?
A: Yes, SourceGrid is compiled with .NET 2 and Visual Studio 2005.

Q: Can I use this project with Visual Studio 2003?
A: No, SourceGrid is compiled with .NET 2 and Visual Studio 2005. If you want you can use an older version (like SourceGrid 3).

Q: How can I create a tree view with SourceGrid? (hierarchical view)
A: For now SourceGrid doesn't support this kind of view. I hope to work on this for a future release.

Q: Can I use these controls with ASP.NET?
A: No, these are Windows Forms Controls. With Internet Explorer you can host a Windows Forms Control inside an html page, but it is usually not a good solution for web pages.

Q: Can I use these controls with .NET Compact Framework? And with Mono?
A: Currently this project is compatible only with Microsoft .NET Framework and Windows platforms. I'm working for porting some of the code to Mono and Compact Framework. Consider anyway that all the code is written with managed C# so probably it is not too difficult to use this library with other frameworks.

Special Thanks

A special thanks to all who have helped to make this project with suggestions, bug reports, ideas and code.
I've also benefited greatly from:

Support - How to ask for help

History



4.22 (xx xxx 2009)



4.21 (03 Jun 2009) Beta



4.20 (17 Jan 2009)

4.11 (27 Nov 2007)

4.10 (16 Oct 2007)

4.9 (17 June 2007)

4.8 (6 May 2007)

4.7 (16 April 2007)

4.6 (15 April 2007)

4.5 BETA (18 March 2007)

This is a major release with many inportant changes and improvements. Consider that this is a BETA release and probably there are still some bugs and problems. This version can can be unstable. Use the forum for submitting bugs or if you have problems.

4.0.0.4 BETA (10 Nov 2006)

4.0.0.3 BETA (13 October 2006)

4.0.0.2 BETA (08 October 2006)

4.0.0.1 BETA (01 October 2006)

SourceGrid 4 is the evolution of the SourceGrid 3 control. The major differences are:

Previous versions

You can find more information about previous SourceGrid versions at this link: http://www.devage.com/

License

SourceGrid LICENSE (MIT style)

Copyright (c) 2006 www.devage.com, Davide Icardi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.