The Program
This section examines the relevant VirtGrid source code. The grid control in this sample displays and allows end users to edit data stored in the document's member object m_data.
The following are code listings and discussions of the relevant overrides in this sample.
Initialization and Column Count
 
void CVirtGridView::OnInitialUpdate()
{
CGXGridView::OnInitialUpdate(); // 1
SetColCount(GetDocument()->m_data.GetColCount()); // 2
SetStyleRange(CGXRange().SetCols(4), // 3
CGXStyle().SetControl(GX_IDS_CTRL_CHECKBOX));
SetStyleRange(CGXRange().SetCols(1),
CGXStyle().SetInterior(RGB(150,150,150)));
SetStyleRange(CGXRange().SetCols(2),
CGXStyle().SetTextColor(RGB(0,0,255)));
GetParam()->EnableMoveCols(FALSE); // 4
GetParam()->EnableMoveRows(FALSE);
m_nClipboardFlags = GX_DNDTEXT; // 5
}
Override OnInitialUpdate to initialize the virtual grid. This override can also be used to initialize other aspects of the grid object.
//1 Calls the base class to allow for default initialization of the grid.
//2 Sets the column count in the grid object based on the column count of the external data source. This allows you to set the base styles for the columns in subsequent lines.
//3 Changes the column base style in the corresponding columns thereby affecting all the cells in those columns. Note that SetStyleRange() cannot be called for individual cells because SetRowCount() has not been called to force the grid object to allocate space for individual cells.
//4 Disallows column and row movement. For ease of explanation in this tutorial, we will not allow columns or rows to be moved. Before columns or rows can be moved, a mechanism must be put in place to track or sync the data source and the grid object. There are two techniques that will accomplish this.
First, an index map could be created to map grid coordinates to the underlying data structure's coordinates. The map would be used in GetStyleRowCol() and StoreStyleRowCol() to retrieve and set the appropriate data from the data source. The second technique is to actually move the data in the underlying data structure.
Both techniques have advantages and disadvantages. The nature of your application and data will determine which technique is more appropriate. In either case, two new virtual overrides will come into play, StoreMoveCols and StoreMoveRows. Please refer to “Drawing and Updating” for more information.
//5 Forces the grid object to copy only the cell's data rather than the full style during clipboard operations. This setting is necessary because the grid is not maintaining individual cell styles.
Row Count
 
ROWCOL CVirtGridView::GetRowCount()
{
return GetDocument()->m_data.GetRowCount(); // 1
}
Override GetRowCount() to furnish the number of data rows to the grid object. This differs from an explicit call to SetRowCount() in that no style objects are allocated for each individual cell. In a virtual grid, the data is stored externally to the grid object making individual style objects unnecessary. An explicit call to SetRowCount() would only waste memory and resources.
//1 Return the row count based on the row count of the external data source.
NOTE >> GetRowCount() and GetColCount() are used extensively to validate values within routines. In addition, because HitTest() is called repeatedly on every mouse move and because this method touches a lot of grid code, including GetRowCount(), it is not unusual for GetRowCount() to be called thousands of times in a short period of time. Because GetRowCount() and GetColCount() are called quite often, your overrides of these methods are not an appropriate place to do extensive calculation.
Making the Virtual Connection Between Grid Object and Data Source
 
BOOL CVirtGridView::GetStyleRowCol(ROWCOL nRow,
ROWCOL nCol,
CGXStyle & style,
GXModifyType mt,
int nType)
{
BOOL bRet = CGXGridView:: GetStyleRowCol(nRow, // 1
nCol, style, mt,
nType);
if(nType >= 0) // 2
{
if(nCol != 0) // 3
{
style.SetValue(GetDocument()->m_data. // 4
GetValueRowCol(nRow, nCol));
}
}
return bRet;
}
 
BOOL CVirtGridView::StoreStyleRowCol(ROWCOL nRow, //5
ROWCOL nCol,
const CGXStyle *pStyle,
GXModifyType mt,
int nType)
{
if(nType == -1) // 6
{
return CGXGridView::StoreStyleRowCol(nRow,
nCol,
pStyle,
mt,
nType);
}
else if(nType >= 0) // 7
{
if(nCol != 0 && pStyle->GetIncludeValue()) // 8
{
GetDocument()->m_data. // 9
StoreValueRowCol(nRow, nCol,
pStyle->GetValue(),
gxOverride);
return TRUE;
}
}
return FALSE;
}
Override GetStyleRowCol() to supply the data dynamically from the external data source. This virtual function is called every time a cell is redrawn on the DC. Getting the data from the data source from within this override will ensure that the grid is in sync with the data source.
//1 Call the base class to gather any base style information.
//2 Recall from the architecture discussion that GetStyleRowCol() is called four times for each cell that is redrawn. The four calls gather the cell specific style, the column base style, the row base style, and the table base style respectively. The nType parameter specifies which call is being made. An nType >= 0 indicates the cell specific style is being retrieved. An nType = -1 indicates a column, row or table base style is being retrieved. We are only concerned with cell specific styles in GetStyleRowCol(), therefore, this line masks out all calls to GetStyleRowCol() for styles other than cell specific.
//3 This check masks out requests for row header (column 0) styles. These are different from row-wide base styles (indicated by nType = -1 and nCol = 0). We are only interested in supplying column headers and individual cell data.
//4 Get the value from the external data source and set it in the composed style.
//5 Override StoreStyleRowCol() to store the data dynamically to the external data source. This virtual function is called when the end user has changed the contents of a cell and the cell is deactivated.
//6 Recall that nType == -1 means that StoreStyleRowCo() was called to store base style information. We are not storing base style information in the external data source therefore this check masks out base style calls and forwards them to the base class.
//7 This masks out calls to store specific cell styles, which we are storing in the external data source.
//8 This line is performing two checks. The first check, nCol != 0, masks out calls to store row header information. The second check, pStyle()-> GetIncludeValue, verifies that the style passed in actually contains a value. In this example, we are storing only cell values to the external data source.
//9 This retrieves the new cell value from the style parameter and stores in the external data source.
VirtGrid Conclusion
The VirtGrid tutorial demonstrates the minimum requirements to virtually bind a grid object. Keep in mind that virtual binding simply means that a grid's data is stored externally to the grid object. The stored data is not limited to cell values. It is possible to store any aspect of a cell's style externally. For the sake of discussion, we demonstrated only cell value storage, but the technique is comparable for all segments of cell styles.