<%@ Page language="c#" AutoEventWireup="false" %> SandGrid Documentation

SandGrid User Guide

Populating with Data

Back to Table of Contents

Some of the key to SandGrid's power and speed lies in the many ways in which it can be integrated into your application. You may wish the grid to be entirely uncoupled from your data model, populating it yourself as if it were a ListView. You can bind it directly to data with standard .net databinding. You can loads hundreds of thousands of loads instantaneously with virtual mode. You can create a GridRow subclass (a "virtual row") that knows about your data model to extract data as needed.

Manual Population with Cells

This is the most basic method of populating a grid and the easiest to get to grips with. Each GridRow has a Cells collection, which you populate with instances of the GridCell class or a specialised subclass. The index of a cell corresponds with the index of a column: the cell at position zero is considered a child of the grid column at position zero. Although not enforced, a cell should match its column in terms of datatype. If a column is expecting integer data a GridIntegerCell should be used. You can ask a column to create a cell of the correct type by calling its CreateCell method.

Call the NewRow method on your grid to create a GridRow already populated with cells matching the columns currently in the grid. You can then add this row to the grid and set its cells' data. For example, if you have three standard columns in a grid here's how you might programmatically create a new row and add it to the grid.

GridRow newRow = sandGrid1.NewRow();
newRow.Cells[0].Text = "Apple";
newRow.Cells[1].Text = "Orange";
newRow.Cells[2].Text = "Banana";
sandGrid1.Rows.Add(newRow);

Or you can express this operation a little more succinctly:

sandGrid1.Rows.Add(new GridRow(new GridCell[] { new GridCell("Pineapple"), new GridCell("Kiwi"), new GridCell("Strawberry") }));

Every row has a NestedRows collection property. This exposes a further collection of rows which can be added to, to create a hierarchical tree of rows within the grid - like a TreeView. Rows have an Expanded property which indicates whether nested rows are shown, and an expand/collapse button is available for the user too if the relevant grid property is set.

Standard DataBinding

SandGrid supports the standard .net databinding interfaces so it can be bound to datasets and other sources in the same way that other controls can. The familiar DataSource and DataMember properties are used and can be configured at design-time too. The IList, IBindingList and IBindingListView interfaces are supported, along with ITypedList for discovery and display of hierarchical data. When binding to a source with multiple sub-sources the DataMember directs the control to the initial table to display.

When databound, you cannot add or remove rows programmatically. Changes to your data will be detected automatically through notification APIs and the display should refresh automatically. Any attempt to manually modify rows will result in an exception.

The DataBindingComplete event is fired on the grid whenever any databinding update is finished, whether it is initial population or a value change. The type of change is signalled to the event handler and the consuming application is then free to make any minor updates to displayed data. For instance, it might go through setting the CheckState of each row. The object to which any row is bound is available through the row's DataItem property.

Virtual Mode

SandGrid fully supports virtual mode, a term used for automatic and instant population of the grid where a very large number of rows are needed. In this mode, manual population is used (see previous) but the grid creates the rows instead of the application, letting the application know when a row needs to be populated with data. Behind the scenes, the grid may look like it's holding thousands or millions of rows but in fact only the rows onscreen are kept in memory; the others being periodically discarded. This means that memory consumption is not an issue.

To enable virtual mode, set the VirtualMode property of your grid to true and set the VirtualRowCount to the number of rows that should be present. You can use the VirtualRowSize property to control the height of the rows that are generated if you need to. You will need to handle the PopulateVirtualRow event, and in the handler use application-specific logic to fill the row passed to the handler with data appropriate to the index of that row. This handler is also where you would configure any formatting for the row. Note that the row has already been populated with cells at this point.

The vast speed and memory gains of virtual mode are made possible by making certain assumptions about the data display. All rows must be the same height, features like nested grids, nested rows and grouping are not available. The good news is that these features are not very practical when dealing with hundreds of thousands of rows anyway.

Because rows are created and discarded on the fly, your application must always be able to populate a row given its numeric index in the grid. Once populated, if the user scrolls away then back again the row will probably be discarded and you'll need to populate it again. Here's an example of how you might respond to the PopulateVirtualRow event by simply setting textual cell content that shows the index of the row.

private void sandGrid1_PopulateVirtualRow(object sender, Divelements.SandGrid.GridRowEventArgs e)
{
	// Obtain the index of the row in the grid
	int rowIndex = e.Row.Index;

	e.Row.Cells[0].Text = "This is row " + rowIndex + ".";
}

Virtual Rows

Not to be confused with virtual mode, virtual rows is a concept unique to SandGrid and is probably the most powerful way of integrating the grid with an application. It involves writing a little code: you need to write a small class that inherits from GridRow and overrides (at a minimum) the GetCellValue method. This method is always called by SandGrid and its base implementation retrieves the value from a cell, or from a databound object, depending on the grid configuration. When you write a virtual row, the Cells collection is empty because no GridCell instances are required; you can get the data dynamically from wherever you want.

An example usage of virtual rows would be an email application. If you wanted to display a list of emails you would probably already have a list of Email classes, your business objects with properties and methods but no display logic. Using virtual rows you could create a class inherited from GridRow called EmailRow (for example) that accepted an Email instance in its constructor, which it would store. In the overridden GetCellValue method it would look at the column wanting data and return the appropriate data from the function.

We provide a static DataUtilities.BindToClassInstances method that helps with populating a grid will of virtual rows. It is not necessary to use this helper method but it is a quick way of passing a grid, a list of business objects and a virtual row type and having all the rows instantiated and loaded into the grid. The method can also recurse on hierarchical data if needed.

This method makes it very easy to return dynamically-calculated data too, as your code to obtain the data can do whatever it likes. You can also override GetCellImage to dynamically decide on an appropriate image for a cell, or DrawVirtualCell which allows you to dynamically intercept the drawing of a cell and override the default formatting; perhaps making a cell bold or red in response to criteria on the business object.

What about data that changes? There is a corresponding SetCellValue method which you can override if the user is able to edit your data. If your class detects that something about the object it represents has changed it can call the protected NotifyColumnValueChanged method to tell the grid a value has changed.

Several of the example windows in the demonstration application use virtual rows.

Combining Population Methods

The methods described so far on this page can be combined. This is for users who really know what they are doing and want to squeeze just a little bit more control out of a grid that is already displaying data.

Standard databinding can be combined with virtual mode. To do this, simply switch on the VirtualMode property when the grid is databound. Do not attempt to set the VirtualRowCount as this will be set automatically from the bound datasource. Combining these two population methods results in lightning-fast binding to existing data with thousands or millions of rows.

The concept of inheriting from GridRow to finely-control data display, normally known as virtual rows, can also be used when databinding or virtual mode is being used. Set the NewRowType property of your grid to the type of your GridRow subclass to have the grid create an instance of that when new rows are being created. You can then use the techniques from virtual mode in these other scenarios.

Next: Working with Columns