Dapfor’s developers tried to combine intuitive and easy to use grid interface with ability to create complex and feature-rich applications. Most efforts were applied to the hierarchy building area. Grid developers decided to move away from traditional hierarchy model based on bands as it is bulk and inconvenient and has poor performance. Instead, the grid supports work with one or multiple headers that can instantly transform the grid from treelist control (such as windows explorer) to fully functional grid with multiple independent headers without changing data structure inside the grid.


Every header has two panels – the grouping panel and the column panel. The grouping panel contains columns with Column.Grouped property set to true, while the column panel contains only visible columns, i.e. only columns that have Column.Visible property set to true.

grid-intro 4

Every column has its own identifier that is unique within the header. It stores information of width, sorting direction, text and image alignment and appearance of column and its cells. Columns are added to the header as follows.

C# Copy imageCopy
Header header = new Header();
header.Add(new Column("price", "Price label", 100, ContentAlignment.MiddleLeft));
header.Add(new Column("quantity", "Qty", 100));


The following code demonstrates removal of one or all columns:

C# Copy imageCopy
Header header = grid.Headers[0];
//Remove a column
Column column = header["price"];

//Remove all columns

It is also possible to move columns within header using programming means.

C# Copy imageCopy
column.VisibleIndex = header.VisibleColumns.Count - 1;

The header can display columns at their actual width. If overall width of visible columns in a header exceeds size of visible grid area, the grid displays a horizontal scrollbar that supports two modes of work. In the first mode (ColumnScrollType.Optimized), when there are multiple headers in the grid, the grid stops scrolling columns in a header irrespective of thumb position, if the last column becomes fully visible. This way, it can display the highest volume of information in columns and cells. In the second mode (ColumnScrollType.Normal), the grid scrolls columns according to thumb position and displays empty space after the last column.

C# Copy imageCopy
Header header = grid.Headers[1];
header.ScrollType = ColumnScrollType.Optimized;
Horizontal scroll in optimized mode

The grid supports stretching of visible columns to maximum width of visible area. In this case, there is no need to display a horizontal scrollbar.

C# Copy imageCopy
grid.Headers[0].StretchMode = ColumnStretchMode.All;
grid.Headers[1].StretchMode = ColumnStretchMode.All;
Stretched columns

The header has multiple collections of columns: Header.VisibleColumns, Header.GroupedColumns, Header.MergedColums. All these collections provide most common mechanisms for column access and enumeration.

Merged columns

A new feature in version 2.8.0 enables column merging in groups. Columns can be moved and sorted and their size can be changed within a group. However, the grid doesn’t support moving columns outside the group or inserting a column not belonging to the group into the middle of this group. Merged columns cannot be grouped, but otherwise they behave as regular columns.

C# Copy imageCopy
Header header = grid.Headers[0];
MergedColumn mergedColumn = header.MergedColums.CreateNew("Merged column");

header.ColumnPanelHeight = 36;
mergedColumn.Height = 18;
Merged columns

Disabling column changing via UI

A programmer designing an application may wish to prevent users from changing column positions, size, sorting, grouping, etc. For this purpose, every column has properties that prevent users from manipulating headers.

C# Copy imageCopy
column.Configurable = false;
column.Movable = false;
column.Resizable = false;
column.Groupable = false;
column.Editable = false;
column.Sortable = false;

Header context menu

Header supports displaying context menus with different sets of ToolStrip items depending on clicking position and on header and column settings. This process is very easy to control. The following code shows how to add new items with event handlers.

C# Copy imageCopy
grid.HeaderContextMenu.Opening += delegate(object sender, CancelEventArgs e)
    ContextMenuStrip contextMenu = (ContextMenuStrip) sender;

    contextMenu.Items.Add(new ToolStripSeparator());
    contextMenu.Items.Add(new ToolStripMenuItem("My item", null, delegate
        //The user has clicked on my item, 
        //do something here!
Header context menu


Every header and every row in the grid have their hierarchy level starting from 0. When displaying cells, the grid searches for header that corresponds to the same hierarchy level as the row. If there is no such header, a header of the previous level is used.

C# Copy imageCopy
Header header1 = new Header();
Header header2 = new Header();

Assert.AreEqual(0, header1.Level);
Assert.AreEqual(1, header2.Level);

grid.DataSource = someSource;
Row row = grid.Rows[0];
Assert.AreEqual(0, row.Level);

Row childRow = row.Add(new string[] {"subitem1", "subitem2"});
Assert.AreEqual(1, childRow.Level);

This way of data presentation with a single header often reminds Windows Explorer.

To transform this component to a grid with multiple headers you just need to add one or more headers without changing the data structure. When a new header starts corresponding to a row, the grid displays row cells for a new header in a new way. However, data structure and hierarchy won’t change. In this architecture headers can be changed dynamically without modification of data structures. It is possible to create multiple headers with different number of columns, IDs, sorting and grouping to display different data object fields and to change these headers dynamically in runtime.

C# Copy imageCopy
//First header
Header header1 = new Header();
header1.Add(new Column("Id1"));
header1["Id1"].Format = new StringFormat("dd-MM-yyyy");
header1.Add(new Column("Id2"));

//Second header
Header header2 = new Header();
header2.Add(new Column("Id1"));
header2.Add(new Column("Id3"));
header2["Id1"].Format = new StringFormat("yyyy-MM-dd");

//Change headers without changing data


Serialization features are also available in runtime. Header class has Header.SerializationState property that returns an object implementing two interfaces: ISerializable and IXmlSerializable. These two interfaces can easily save header state to binary or XML files or streams and restore it from these files.

C# Copy imageCopy
Header header = grid.Headers[0];
Header.HeaderSerializationState state = header.SerializationState;

using (FileStream fs = new FileStream(@"Test.xml", FileMode.OpenOrCreate, FileAccess.Write))
    XmlSerializer serializer = new XmlSerializer(typeof(Header.HeaderSerializationState));
    serializer.Serialize(fs, state);

Test.xml file:

C# Copy imageCopy
<?xml version="1.0"?>
<header ver="2" level="0">
    <column ver="2" id="Id1" name="Id1" index="0" visIndex="0" width="60" format="Dapfor.Net.TypeConverters.StringFormatConverter;FormatString=dd-MM-yyyy" flag="0" />
    <column ver="2" id="Id2" name="Id2" index="1" visIndex="1" width="60" flag="0" />

Deserialization from Test.xml:

C# Copy imageCopy
using (FileStream fs = new FileStream(@"Test.xml", FileMode.Open, FileAccess.Read))
    XmlSerializer serializer = new XmlSerializer(typeof(Header.HeaderSerializationState));
    Header.HeaderSerializationState state = (Header.HeaderSerializationState) serializer.Deserialize(fs);

    Header header = new Header();
    header.SerializationState = state;
    Column column = header["Id1"];
    Assert.AreEqual(60, column.Width); 

An object returned by Header.SerializationState can be transferred to any other header enabling easy copying of all columns with information on their visibility, position, size, sorting, grouping and appearance.

C# Copy imageCopy
grid1.Headers[0].SerializationState = grid2.Headers[0].SerializationState;