View RSS Feed

Development Team Blog

The New Grids - All About Rows and Columns

Rate this Entry
The information about row and column changing is still being documented. There are significant changes in the way the new grid works as compared to the old grids. Here is a summary that should help everyone get started.

Navigation consists of changing columns and changing rows. A navigation event consists of a row change and/or a column change. It is best to think of these as two events.

Column Change

When a column change occurs, events are sent to the column objects involved in the change.

OnExiting - called before exiting, can be canceled

OnEntering - called before entering new column, can be canceled

OnExit - called after the exit event has occurred

OnEntry - called after the column entry has occurred

The current column is accessed via SelectedColumn. It is possible to have no selected column, in which case, SelectedColumn is -1. It is possible to change from a non-selected column to an actual column (in which case, the exiting events are not sent) and it is possible to change from an actual column to a non column (in which case, the entering events are not sent).

Column changes occur within a single row and they always apply to the SelectedRow.

Row Change

When a row change occurs, the following events are sent (note that as of the first alpha, these events are missing - they are coming and it makes sense to talk about them. All of the other row change logic is already in place).

OnRowChanging - called before the change and can be used to cancel the row change

OnRowChanged - called after the change and can be used when you know the row change has occurred

When a row change occurs, the SelectedColumn is always set to -1, which fires the the column exiting event. The row change logic then occurs, which can involve saving a row or resetting the row back to its old values. You always have to do one or the other. If the row-change fails, the previous SelectedColumn is reset causing the column entering events to be fired. If the row change succeeds, a new SelectedColumn will be selected if this is part of the focus change event -and it usually is.

For example, if you move down one row while staying in the same column, SelectedColumn will be set to -1, SelectedRow is changed, and SelectedColumn is set back to the same column. This causes column exiting to be sent to the old row and column entering to be sent to the new row.

A successful row change changes the value of the datasource object's SelectedRow.

If a save occurs during a row change, all columns are validated via OnValidating, which can be used to cancel the row save, which will cancel the change.

SelectedRow and SelectedColumn

The grid is always SelectedRow oriented. You are not allowed to change the SelectedRow without successfully saving or clearing changes in the row. This should be familiar to everyone who is used to working with the old dbGrid. However, this rule is applied to both cDbCJGrids and regular cCJGrids. In fact, all of the above entering, exiting, validating logic applies to cCJGrids. What is different are the rules that the datasource object uses for saving data (cCJGridDataSource for cCJGrid, and cDbCJGridDataSource for cDbCJGrid). With a data aware cDbCJGrid a save means saving the data to a back end database. With cCJGrid, a save simply means that the datasource's internal data array accepts the new data.

If you are using a cCJGrid with no validations, verifications, exiting or entering, which is the default, it just feels like you entering data directly into the grid. All of the SelectedRow row-save orientation is hidden. In such a case, you really don't need to worry about any of the above information.

If you are using a cDbCJGrid, the grid and its datasource get most of what they need from your data dictionaries. The data dictionaries provides all of the entering, exiting, validating, saving, deleting and finding logic you need without any extra code on your part. In such a case, you really don't need to worry about any of the above information.

The focus change is controlled by the COM control's OnComFocusChanging event. This contains the heart of the entire row/column change logic and we rely on this working in an exact manner. You want to avoid augmenting this event.

Working with SelectedRow and SelectedColumn

You always want to work with SelectedRow and SelectedColumn and you do not want to use the rows/columns provided by the COM control. This is crucial. Almost everything the grid does is based on the SelectedRow of the datasource object (phoDataSource). The SelectedRow must stay synchronized with the grid's focus row and it must stay synchronized with the data back-end (e.g., with a cDbCJGrid, it must be synchronized with the current record of the data dictionary object).

It is possible to have no row selected and no column selected. If there is no row, the SelectedRow of phoDataSource will be -1. If there is no column, the SelectedColumn of the grid object will be -1. If column focus is not allowed (pbFocusSubItems=False), SelectedColumn will always be -1.

SelectedRow and SelectedColumn are not properties that can be set, they are functions.
Handle hoDataSource
Integer iSelRow iCol
Get phoDataSource to hoDataSource
Get SelectedRow of hoDataSource to iSelRow
Get SelectedColumn to iCol
The grid class controls these values. As you navigate through the grid, these are maintained. You can change rows and columns manually using various messages provided for that purpose, such as MoveToRow, MoveToFirstRow, MoveToLastRow, MoveToColumnObject, MoveToFirstEnterableColumn, MoveToLastEnterableColumn, Next and Previous. These all maintain SelectedRow and SelectedColumn. Stay away from the COM navigation methods.

In general you also want to stay away for the COM row and item messages that get or set data in the grid. The grid is virtual and the COM rows and items are temporary virtual representations of the data. For example, if the SelectedRow is 4, you cannot get the COM row values in row 3, because they don't really exist. The real data is in the datasource object and even that may just be a partial cached version of your back-end data.

Column Objects

The column object interface provides easy and abstracted access to data in your datasource. Most of the column object interface operates on the SelectedRow. All of the entering, exiting, validation and editing events do this. You can get the SelectedRow's value for a column with the SelectedRowValue message. You can change that value using UpdateCurrentValue message. You will rarely need to access the column value of a non-SelectedRow but there are times when you will need this (e.g., creating a custom info-tip when you hover over a row). This can be done with the RowValue message. And, of course, you cannot alter the value of a non-SelectedRow.

Column processing (set-up, navigation, events) is performed by sending messages to column objects and not to column numbers. If you need information about a particular column, you will probably identify it by its object name. If you need to send a message to the SelectedColumn you can get the column object with the SelectedColumnObject message. Avoid using hard-coded column numbers. The grid allows you to reorder columns and hide columns. This makes accessing information by column number confusing, because you have to deal with creation order versus display order. Column objects makes this all much easier. This is also a reminder to not assume that you know what the next and previous column will be, as a user can change this.

Don't think of the new grid as an item based system and stay away from item change type of logic. The old grids were just collections of items. The new grids are collections of rows that contain collections of columns. Row changes and column changes are treated as two different events. One can occur without the other. One can succeed while the other fails. The OnComFocusRowChanging event may send a row change and a column change as two sets of events. For example, if you are in row 1, column 1 and you click on row 2, column 2, the notification of this may not occur in a single event - this is the way the control works. Therefore, you are best off treating row changing and column changing as distinct events.

We think that this row, column orientation is a much better model, but it may not be what you are used to. In most cases, this makes no difference because you don't work at this level anyway - that's what the data dictionaries are for. If you do need to some perform more advanced navigation logic, this new design should be much more robust.

[Note: Comments are disabled for this article. Comments should be directed to the Visual DataFlex 2010 Testing forum.]
Tags: grids, john tuohy