Grid

Grid is a new widget for the classic VW GUI that combines elements of the Table and Dataset widgets for a simpler and more flexible interface. It is based on the Grid from the Widgetry project.

It consists of three packages:

  1. Grid -- Base UI Grid support package.
  2. Grid UIPainter -- UIPainter interface add-on for Grid.
  3. Grid Samples -- Sample Grid and test applications.
  4. Grid SUnit Tests -- SUnit test classes and empty Grid application for interactive tests.
Grid Features Current and projected features.
Grid Elements Grid support classes.
Grid Column Models How to setup a column model.
Grid UIPainter Interface Creating and editing Grid attributes in a canvas.
Grid Navigation Scrolling and keyboard navigation to a new destination.
Grid Selection Selecting rows or cells in a Grid.
Grid Sorting Row sort with and without a UI.
Grid Drag and Drop Reorder columns or move and insert rows
Grid Interface Application layer interface object that simplifies Grid setup and use.
Grid Tutorial Simple example Grid creation and manipulation.

Grid Features

Grid Preview Features

Grid Planned Features


Grid Elements

New for a Grid are classes defined to represent and take the roles of its rows, columns, and cells. This improves the accessibility and control of a Grid by one or more of its elements. The DataSetView and TableView were sorely lacking in this encapsulated behavior.

Visual Elements

The Grid itself is a CompositeView that contains only the elements visible in its pane. Its components are dynamically added and removed as the bounds of the Grid pane changes or is scrolled. As the counterparts of a Grid's persistent elements these visual elements are built and have a lifetime only while elements are visible in the pane. These classes are private for use within the Grid.

GridVisualSegment
       GridVisualRow
       GridVisualColumn

A GridVisualSegment is the abstract superclass of a GridVisualColumn and a GridVisualRow. It knows of and can access its adjacent visible neighbor in a linked list. If a GridVisualSegment has no predecessor or successor then it resides at the visible or scrollable border of a Grid pane. If the segment is a GridVisualRow it knows its top and bottom position in the Grid, its GridRow, and its height. If the segment is a GridVisualColumn it knows its left and right Grid position, its GridColumn, and its width.

GridVisualCell -- Visual counterpart of GridCell that constructs and maintains a Grid component using the Grid's builder.


Column Models

The model for a GridColumn specification defines the aspect path required to obtain the value for a cell in the column for any row. Although the model may be provided directly as an AspectAdaptor whose subject is the row item, typically it is specified as a Symbol for the aspect path that the Grid cell builder will use to create and assign as model. When specified as a Symbol, it is declared in this format

#'_row *<unary accessor>'

where '_row' is a reserved word denoting the model will be accessed from the current row item and '*<unary accessor>' is one or more unary accessor messages that form the aspect path to obtain the value for the cell from the row item.

In use, the GridColumn spec declaration might appear as

(GridColumnSpec new
        label: 'First Name';
        width: 80;
        spec: (InputFieldSpec new model:#'_row firstName');
        yourself)

Examples:

#'_row firstName' The value is obtained by sending #firstName to the row item. The row item must also respond to #firstName: when the cell value is set by an editor.
#'_row address state' The value is obtained by sending #state and set by sending #state: to the object obtained by sending #address to the row item.
#'_row 2 y' The value is obtained by sending #y to the second element of the row item. The row item is most likely a SequenceableCollection of Points and this aspect path will access or modify the 'y' value of its second element.

If the cell model is to be buffered for changes the model may be declared in this format:

#'_row *<unary accessor> | <buffered aspect>'

where '<buffered aspect>' is the aspect for a ValueModel which when set true accepts the edited value from a buffer. If the '<buffered aspect>' model is set to false the last edited value in a buffer is canceled and the value is restored from the row item.


Grid UIPainter Interface

The Grid-UIPainter package/parcel adds code to the UIPainter canvas editing tools to easily add and edit a Grid widget. Load the Grid-UIPainter and it should include a Grid widget button among the other widgets available for selection on the palette.

The Grid-UIPainter component enables the following editing tasks:

Adding a column

Select the Grid widget and the PainterTool should show a Basics page. Press the New Column button to add a new column. Each new column will add a child node to the Grid widget appearing in the PainterTool hierarchical tree of components in the canvas.

Selecting a column

Either select the Grid child node appearing for the column in the PainterTool tree or <Alt>-<Select> the desired column in the Grid appearing in the UIPainter canvas. The selected column will appear highlighted and the PainterTool pages will change to display and enable editing of a column's attributes. If the content for a column has not been defined only the Basics page will appear.

Define column content

A column may contain any widget but only widgets that have models are useful. Defining a column with a label, view holder, region, resizing splitter, and group box will only show a column of identical items. Likewise, a column of dataset, click widget, chart, notebook, tab widget, subcanvas, and even grid widgets are to be avoided since they unnecessarily complicate an interface. The most useful and recommended widgets for column content are input fields, text editors, spin buttons, combo boxes, check boxes, and sliders.

Select the Grid widget in the canvas then select the widget you would like to define in the UIPainter palette. Drop your selected widget onto a column by clicking on it. This defines the content for the column. If the column had no content defined for it previously then additional pages besides Basics will appear in the PainterTool to edit the attributes of the content (e.g. Component, Color, Details, etc). If the Grid widget is not first selected then dropping a widget from the palette on the Grid will simply place the widget on top the Grid.

Removing a column

Select a Grid column either from the PropertiesTool tree or by clicking on the column using <Alt>-<Select>. Press the Remove Column button on the Basics page the the column will be removed.

Reordering columns

Select a Grid column in the canvas then drag and drop a column by its header to a new position among other columns.

Column width

Drag a column border with the mouse to resize it. Currently there isn't an entry field on the column Basics page to set the size to a particular width in pixels.

Row height

Row height is only defined and relevant for rows added to a Grid at runtime. One may set the default row height in the Grid Basics page--the height a row will assume if it isn't individually assigned a height.

Adding a row header column

Select the Grid and the PropertiesTool Basics page. In the Rows group box, select Add Header. This should add a leftmost row selector column to the Grid. This should also enable the Buttons and Numbered check box options to change the style of the row selector column. If you select Numbered for a numbered row selector column then a non-zero entry to Offset will offset the initial row number by the number given.

Adding a column header row

Select the Grid and the PropertiesTool Basics page. In the Columns group box, select Add Header. To edit each column name select a Grid column either from the PropertiesTool tree or by clicking on the column using <Alt>-<Select> then enter its name in the column Basics page.

Overall row and column behavior

Modify the following check boxes on the PainterTool Basics page to change the following column and row attributes:

Columns
Allow Resizing
If on, any column may be resized interactively by dragging its border.
Allow Reordering
If on, columns may be interactively reordered by dragging and dropping them by column header.
Rows
Allow Resizing
If on, rows may be resized by interactively dragging their borders.
Height
Define a default row height in pixels in this field.


Grid Navigation

Keyboard Navigation Shortcuts and Programmatic Counterparts

Action Keypress Grid Message Notes
Scroll page up <PageUp> aGrid pageUp  
Scroll page down <PageDown> aGrid pageDown  
Scroll to top line <Control>-<Home> aGrid home  
Scroll to bottom line <Control>-<End> aGrid end  
Edit next field <Tab> aGrid moveEditHorizontallyBy: 1 aGrid horizontalPolicy ~= #none
Edit prior field <Shift>-<Tab> aGrid moveEditHorizontallyBy: -1 aGrid horizontalPolicy ~= #none
Edit upper field <Up Arrow> aGrid moveEditVerticallyBy: 1 aGrid verticalPolicy ~= #none
Edit lower field <Down Arrow> aGrid moveEditVerticallyBy: -1 aGrid verticalPolicy ~= #none

Row Scrolling

Scroll to make the row visible at the closest border aGridRow scrollToView
Scroll to make the row visible at the closest border aGridRow scrollToView: #nearest.
Scroll to make the row visible at the Grid top aGridRow scrollToView: #top.
Scroll to make the row visible at the Grid bottom aGridRow scrollToView: #bottom.
Scroll to make the row visible at the Grid center aGridRow scrollToView: #center.

Column Scrolling

Scroll to make the column visible at the closest border aGridColumn scrollToView
Scroll to make the column visible at the closest border aGridColumn scrollToView: #nearest.
Scroll to make the column visible at the Grid left aGridColumn scrollToView: #left.
Scroll to make the column visible at the Grid right aGridColumn scrollToView: #right.
Scroll to make the column visible at the Grid center aGridColumn scrollToView: #center.

Cell Scrolling

Scroll to make the cell visible at any closest border aGridCell scrollToView
Scroll to make the cell visible at any closest border aGridCell scrollToRowView: #nearest andColumnView: #nearest.
Scroll to make the cell visible at the top left aGridCell scrollToRowView: #top andColumnView: #left.
Scroll to make the cell visible at the bottom left aGridCell scrollToRowView: #bottom andColumnView: #right.
Scroll to make the cell visible at the top right aGridCell scrollToRowView: #top andColumnView: #right.
Scroll to make the column visible at the pane center aGridCell scrollToRowView: #center andColumnView: #center.
Scroll to make the cell visible at the horizontal center aGridCell scrollToRowView: #nearest andColumnView: #center.

"Scroll the editor into view"
aGrid editCell scrollToView.


Grid Selection

Selection Modes

Unlike other selection widgets the Grid was designed with the objective to be flexible enough to change settings for multiple or single selection, cell or row selection without reinitializing selections or models. *Cell selection -- If true, individual cells may be selected, otherwise only entire rows may be selected. *Multiple selection -- If true, more than one entity may be selected at once. !Selection Ranges Since the Grid may allow multiple, individual cells to be selected a truly two-dimensional selection indexing scheme was necessary. Instead of a single selection indexing scheme as is used with other selection widgets the Grid uses a Set of Rectangles to denote regions of selections. A selection region is denoted by the coordinate positions for the origin and corner of a cell selection rectangle. If the Grid is set to accept multiple selections it will merge selection rectangles in its set as selections are added or divide selection rectangles in its set as selections are removed as necessary to keep the selection set simplified.

Selection Range Examples

Selection Range
Line 1 (6 columns) Set with: (1@1 corner: 6@1).
Cell 2@2 Set with: (2@2 extent: 0@0).
Line 1 and 2 (6 columns) Set with: (1@1 corner: 6@2).
Cell 1@1 and 2@2 Set with: (1@1 extent: 0@0) with: (2@2 extent: 0@0).
Cell 1@1, 1@2, and 2@2 Set with: (1@1 extent: 0@1) with: (2@2 extent: 0@0).
Adjacent Cells 1@1 thru 2@2 Set with: (1@1 extent: 1@1).

Interactive Selection

If a RowHeaderColumn appears in a Grid then selecting its row header button at any time will select the entire contents of a row whether the Grid is set for cell selection or not. Note that if a cell is not read-only it cannot be selected interactively since selection will always enter its edit mode. When a Grid is set for multiple selection, multiple items may be sweep selected. Hold the <Shift> down and select the next target by mouse or keyboard and all items between the old and new target will be selected. Alternatively, if <Control> is depressed a selection will be toggled (i.e. added if absent or removed if present). The keyboard may be used to toggle selection of the target position by pressing <Space>.

Setting Selections Programmatically

Send #selectRange:, #unselectRange:, or #resetSelections to a Grid to add, remove, or clear selection ranges from its selection set.

Selection Range Examples

"Single select, row selection. 7 columns"
grid multiSelect: false.
grid selectByCell: false.

Action Code Resulting Selection Set
Add row 13 grid selectRange: (1@13 extent: 0@0). Set with: (1@13 corner: 7@13)
Change to row 14 grid selectRange: (1@14 extent: 0@0). Set with: (1@14 corner: 7@14)
Change to row 16 grid selectRange: (1@16 extent: 0@0). Set with: (1@16 corner: 7@16)
Change to row 15 grid selectRange: (1@15 extent: 0@0). Set with: (1@15 corner: 7@15)
Clear all selections grid resetSelections. Set new

"Multi select, row selection. 7 columns"
grid multiSelect: true.
grid selectByCell: false.

Action Code Resulting Selection Set
Add row 13 grid selectRange: (1@13 extent: 0@0). Set with: (1@13 corner: 7@13)
Add row 14 grid selectRange: (1@14 extent: 0@0). Set with: (1@13 corner: 7@14)
Add row 16 grid selectRange: (1@16 extent: 0@0). Set with: (1@16 corner: 7@16) with: (1@13 corner: 7@14)
Add row 15 grid selectRange: (1@15 extent: 0@0). Set with: (1@13 corner: 7@16)
Clear all selections grid resetSelections. Set new

"Single select, cell selection"
grid multiSelect: false.
grid selectByCell: true.

Action Code Resulting Selection Set
Add cell 1@13 grid selectRange: (1@13 extent: 0@0). Set with: (1@13 extent: 0@0)
Change to cell 1@14 grid selectRange: (1@14 extent: 0@0). Set with: (1@14 extent: 0@0)
Change to cell 1@16 grid selectRange: (1@16 extent: 0@0). Set with: (1@16 extent: 0@0)
Change to cell 1@15 grid selectRange: (1@15 extent: 0@0). Set with: (1@15 extent: 0@0)
Clear all selections grid resetSelections. Set new

"Multi select, cell selection"
grid multiSelect: true.
grid selectByCell: true.

Action Code Resulting Selection Set
Add cell 1@13 grid selectRange: (1@13 extent: 0@0). Set with: (1@13 extent: 0@0)
Add cell 1@14 grid selectRange: (1@14 extent: 0@0). Set with: (1@13 extent: 0@1)
Add cell 1@16 grid selectRange: (1@16 extent: 0@0). Set with: (1@16 extent: 0@0) with: (1@13 extent: 0@1)
Add cell 1@15 grid selectRange: (1@15 extent: 0@0). Set with: (1@13 extent: 0@3)
Clear all selections grid resetSelections. Set new

"Setting selections then merging them. It normally isn't necessary to explicitly request a selection range merge when ranges are added/removed one at a time"
grid multiSelect: true.
grid selectByCell: true.
grid selections: (Set with: (1@43 extent: 0@0) with: (1@44 extent: 0@0) with: (1@46 extent: 0@0) with:(1@45 extent: 0@0)).
grid mergeSelections.
grid selections. "Set with: (1@43 extent: 0@3)"

Removing Selections

"Multi select, cell selection"
grid multiSelect: true.
grid selectByCell: true.

Action Code Resulting Selection Set
Add cells 1@13 to 1@16 grid selectRange: (1@13 extent: 0@3). Set with: (1@13 extent: 0@3)
Remove cell 1@14 grid selectRange: (1@14 extent: 0@0). Set with: (1@13 extent: 0@0) with: (1@15 extent: 0@1)
Remove cell 1@16 grid selectRange: (1@16 extent: 0@0). Set with: (1@13 extent: 0@0) with: (1@15 extent: 0@0)
Remove cell 1@15 grid selectRange: (1@15 extent: 0@0). Set with: (1@13 extent: 0@0)
Clear all selections grid resetSelections. Set new

Dos and Don'ts

Do Don't
Use the Grid selection API methods to add or remove selection ranges. This ensures the Grid UI will update and the selection set appears in minimal form.

grid selectRange: aRectangle
Add or remove selection ranges directly to the Grid selection set. The Grid will not update to show new selections, line and multiple selection rules for Grid #selectByCell or #multiSelect: attributes will not be obeyed, and the selection range may not be in minimal form.

grid selections add: aRectangle

Enumerating Selections

Grid currently has #selectionDo: available to enumerate through all row selections in a Grid. Typically this method is implemented in models such as SelectionInList or SelectionInTree. In the future when Grid has a dedicated SelectionInGrid model the SelectionInGrid instance will be expected to perform this instead. The argument is expected to be a one or two argument block. When #selectionDo: is a single argument block its argument is expected to be the item selected as it is other implementations of #selectionDo: for SelectionInList and others. If it is a two argument Block the first argument will be the selected GridRow instance and the second will its item. Note that #selectionDo: always enumerates over a full row even if only a single cell in the row is selected for a Grid set for cell selection.

"Single argument blocks are passed each whole selected row item"
itemsSelected := OrderedCollection new.
grid selectionDo:[[:item| itemsSelected add: item].
"Dual argument blocks are passed each the selected GridRow and its item"
rowsSelected := OrderedCollection new.
itemsSelected := OrderedCollection new.
grid selectionDo:[[:row :item|
    rowsSelected add: row.
    itemsSelected add: item].
Grid implements #selectionCellDo: for enumerating all cell selections, including those within a full row selection. Like #selectionDo: it accepts one or two argument blocks. This will also be the method implemented by a future SelectionInGrid model. If a single argument block is used with #selectionCellDo: its argument is expected to be the value of a selected cell. When a two argument block is used the first argument is the selected GridCell instance and the second is its cell value.
"Single argument blocks are passed the value of selected cells"
valuesSelected := OrderedCollection new.
grid selectionCellDo:[[:value| valuesSelected add: value].
"Dual argument blocks are passed each the selected GridCell and its value"
cellsSelected := OrderedCollection new.
valuesSelected := OrderedCollection new.
grid selectionCellDo:[[:cell :value|
     cellsSelected add: cell.
     valuesSelected add: value].

Grid Sorting

The Grid can conduct multiple column sorts upon its rows without ever being open as a window. It may also conduct single or multiple column sorts interactively by toggling the sort direction of a column header button. Unlike the DataSetView, no sort method need be defined for a Grid to sort its column. For a column to be sortable it must:

  1. Define a model that answers a Magnitude that responds to #>.
  2. The column must also contain a widget that displays a Magnitude that responds to #>. This excludes widgets that have a Boolean model such as a Checkbox or RadioButton. This includes widgets such as an InputField or SpinButton. When the widget is an InputField the format and precision of the object displayed is what is used for sorting.
Each GridColumn defines a sort constraint instance which may be set for sort direction: ascending, descending or, if nil, no sorting at all. One GridColumn sort constraint may be logically combined with a secondary sort constraint when the primary constraint has two items that match. A GridColumn sort constraint may be used in place of a sort Block for a SortedCollection or a SequenceableCollectorSorter on the rows of a Grid.

A multiple sort attempt is unlikely to be meaningful when there are no two items that match under a primary sort. For example, attempting to sort a database of US Congressmen by house address and last name would not yield any different sort than a single sort would by house address. This presumes no two congressmen live in the same house. Presuming there are more than one Congressman with the same last name then sorting by last name and then by house address might be helpful. Several Congressmen may represent a single state and each state representative may represent a different party--Democrat, Republican, or Independent. Sorting by state and then by party is likely to be a meaningful multiple sort.

Sort Example without a UI

For this example you will want to use the file congress.str in Smalltalk #readFrom: format. This file provides over 400 records of congressmen information of the 2007-2008 US Congress as "screen scraped" from the US House of Representatives Office of the Clerk (http://clerk.house.gov). It provides the sample data the for Grid sort example below. All three Grid packages Grid, Grid SUnit Tests, and Grid Samples should be loaded.

"Sorting"
"Create columns and add to a Grid that will not be opened. Note the model for each of the columns contains an AspectAdaptor"
grid := Grid new.
congress := CongressTable new readMembers.
firstName := (GridColumn spec: (ReadFieldSpec new model:#'_row firstName')) addToGrid: grid.
lastName := (GridColumn spec: (ReadFieldSpec new model:#'_row lastName')) addToGrid: grid.
mi := (GridColumn spec: (ReadFieldSpec new model:#'_row middle')) addToGrid: grid.
state := (GridColumn spec: (ReadFieldSpec new model:#'_row state')) addToGrid: grid.
district := (GridColumn spec: (ReadFieldSpec new model:#'_row district')) addToGrid: grid.
rows := congress members.

"Sort rows by state"
state sortConstraint sortDirection: #ascending. "Set the state column to sort in ascending order"
SequenceableCollectionSorter sort: rows using: state sortConstraint. "Sort the congressmen instances by state, AK to WA"

"Sorting by last name"
lastName sortDirection: #descending. "Sort in descending order by last name"
SequenceableCollectionSorter sort: rows using: lastName sortConstraint. "Sort the congressmen instances by last name"


"Multiple sort"
"Sort congressmen by state. For congressmen that appear in the same state sort by last name"
state sortConstraint sortDirection: #ascending. "Set the state column to sort in ascending order"
lastName sortConstraint sortDirection: #descending. "Sort in descending order by last name"
SequenceableCollectionSorter sort: rows using: state sortConstraint & lastName sortConstraint.

Interactive Sort Column Selection

The GridHeaderRow defines a sort size (i.e. sortSize) property for the maximum number of columns that may be sorted at once. If this property is set above 0 the first distinct sortSize number columns selected in a Grid will be sorted with the first selected column as the primary sort constraint, the second selected column as the secondary constraint, etc. When exactly sortSize number columns have been selected, selecting a new column in the GridHeaderRow resets all sort constraints and the new column selected will be the primary sort column. Select the column again to change its sort direction. Select a new column to add a secondary sort column. Continue to select new columns or toggle the sort direction of exising sort columns until sortSize number of columns have been selected again.

"Open window on sorted rows and add a column header with labels"
EmptyGridApp openWith: grid.
firstName label: 'First Name'.
lastName label: 'Last Name'.
mi label:'Middle'.
state label:'State'.
district label:'District'.
party label:'Party'.
grid columns do:[:each| each width: 60].
grid rows: (rows collect:[:each| GridRow on: each height: 20]).
headerRow := (GridHeaderRow new)
               height: 25;
               yourself.
grid addRow: headerRow beforeIndex: 1.

grid allowSorting: true.  "Allow interactive column sorting"
headerRow sortSize: 2.   "Allow sorting on two columns"

"Select the 'First Name' column header to sort congressmen rows by first name."
"Select the 'First Name' column header to toggle sort direction."
"Select the 'State' column header for a secondary sort by state"
"Select either the 'State' or 'First Name' column header again to toggle their sort direction"
"Select a new column. Since the header row has been set to sort only as many as 2 columns selecting a new column clears all sort constraints and sets the new column as the primary (and only) sort constraint"


"What columns are primary and secondary constraints and what are their sort directions?"
headerRow constraintColumns collect:[:each| each label->each sortDirection].


"Reset sort constraints"
headerRow clearConstraints.


Grid Drag and Drop

If enabled, the user may drag and drop columns to reorder them or move, replace, and insert rows. It is always possible to reorder, add, remove, or change columns or rows programmatically outside the application user interface, for example by using the #columns:, #columns, #rows:, or #rows accessors.

Column Reordering

Like the Dataset, Grid columns may be reordered by drag and drop. Simply drag a column header over a target column header where you wish to move it. Reordering all columns may be enabled by setting the Grid #allowColumnReordering: property true or by checking the Grid Basics page "Allow Column Reordering" property in the Painter Tool.
"Prevent columns from being reordered by drag and drop"
aGrid allowColumnReordering: false
Whether or where individual columns are selectively dragged is enforced by an optional #allowColumnMoveFrom:toIndex: that may be implemented in the application hosting the Grid. This message is sent to the application whenever the Grid allows column reordering and a column header is dragged over another column header. The arguments are the index of the column being dragged and the index of the target column.
allowColumnMoveFrom: columnIndex toIndex: destIndex
"Allow only columns 1 and 7 to be interchanged"
^(columnIndex = 7 and: [[destIndex = 1]) or: [[columnIndex = 1 and: [[destIndex = 7]]

Row Drag and Drop

Grid rows may be reordered, inserted, or replaced by dragging and dropping them within the grid.

Grid Interface

A GridInterface combines and manages the grid view, selection model, and column specifications with row data to simplify access and creation of a grid at an application level. It is the top-level interface for creating, updating, and accessing elements of a grid. A GridInterface
  1. Holds the Grid, its SelectionInGrid instance, and a List of data that makes up the rows of the Grid.
  2. Hides creation and management of GridRows under the most common scenarios.
  3. Set the list of row data based on the model and contents of the Grid.
  4. Set the contents of the Grid and model from the List of row data.
  5. Set grid default values (e.g. row height and column width) without accessing the Grid.
  6. Access to column and row data without indexing headers.

Much of the GridInterface functionality requires an operating Grid instance. This usually is supplied upon building a Grid from spec.


Grid Tutorial

For this example you will want to use the file congress.str in Smalltalk #readFrom: format. This file provides over 400 records of congressmen information of the 2007-2008 US Congress as "screen scraped" from the US House of Representatives Office of the Clerk (http://clerk.house.gov). It provides the sample data the for Grid example below.

You will also want to load the "Grid" and "Grid SUnit Tests" packages from Bear.

Create a Grid on the name, state, district, and party from a database of all 2007 US congressmen. Two classes are required: Congressman, an instance of a congressman's identity and contact information, and CongressTable, a manager object that reads and writes Congressman instances by file. A Smalltalk #readFrom: syntax file named congress.str must appear in the default file directory to read the database.

Creating a Grid Programmatically

"Create a Grid and all its GridColumns. All columns are editable input fields for the name, state, district, and party for a list ofCongressmen appearing as rows. EmptyGridApp is a simple window that displays an empty grid that will be customized."

congress := CongressTable new readMembers.
grid := Grid new.
app := EmptyGridApp openWith: grid.
firstName := (GridColumn new)
      spec: (InputFieldSpec new model: #'_row firstName');
      width: 80;
      label: 'First Name';
      yourself.
grid addColumn: firstName.
lastName := (GridColumn new)
      spec: (InputFieldSpec new model: #'_row lastName');
      width: 80;
      label: 'Last Name';
      yourself.
grid addColumn: lastName.
mi := (GridColumn new)
      spec: (InputFieldSpec new model: #'_row middle');
      width: 80;
      label: 'Middle';
      yourself.
grid addColumn: mi.
state := (GridColumn new)
      spec: (InputFieldSpec new model: #'_row state');
      width: 80;
      label: 'State';
      yourself.
grid addColumn: state.
district := (GridColumn new)
      spec: (InputFieldSpec new model: #'_row district');
      width: 80;
      label: 'District';
      yourself.
grid addColumn: district.
party := (GridColumn new)
      spec: (InputFieldSpec new model: #'_row party');
      width: 80;
      label: 'Party';
      yourself.
grid addColumn: party.
"Create GridRows on each Congressman instance and assign the list to the Grid"
rows := congress members asList collect:[:each| GridRow on: each height: 25].
grid rows: rows.

"Add a Grid column header"
grid addRow: ((GridHeaderRow new)
      height: 25;
      yourself)
   beforeIndex: 1.

"Add a Grid row header with buttons and line numbering"
grid addColumn: ((GridHeaderColumn new)
      lineNumbers: true;
      buttons: true;
      width: 30;
      yourself)
   beforeIndex: 1.

"Adjust line numbering for the column header"
grid columns first offset: -1.
grid setVisibleColumns.

"Removing or moving rows and columns"
"Swap party and district columns"
grid moveColumnIndex: 7 toIndex: 6.

"Remove the next 10 rows"
grid removeRowsFrom: 2 to: 11.

"Remove row at index 5"
grid removeRowIndex: 5.

"Examples of changing column attributes without reopening the Grid"
"Disable the party column"
party spec initiallyDisabled: true.
grid setVisibleColumns.

"Change the party column to be read-only"
party spec initiallyDisabled: false.
party spec isReadOnly: true.
grid setVisibleColumns.

"Change the party column label"
party label: 'Affiliation'.
grid setVisibleColumns.

"Change state column width"
state width: 30.

" Column attributes may be saved and restored"
"Save column attributes and widths to a column spec array to be restored later"
columnSpecs := grid columns literalArrayEncoding.

"Restore columns from specs"
grid columns: (columnSpecs asList collect:[:each| each decodeAsLiteralArray]).


Creating a Grid using the UIPainter

Load Grid-UIPainter and open a new canvas. The UIPainter palette should include a new selection for a Grid widget. Drop and layout a Grid in the canvas window.

Next define 6 columns by pressing the New Column button on the Grid Basics page 6 times. Add an input field as content for each column. To do this first select the Grid, then select the input field widget from the palette and drop one on each column.

Select each column in the Grid then enter the label and component model according to the table below

 Column number  Basics-Grid Column Label String  Component-Aspect  
 1  First Name  _row firstName  
 2  Last Name  _row lastName  
 3  Middle  _row middle  
 4  State  _row state  
 5  District  _row district  
 6  Party  _row party  

Leave the ID of the Grid widget as the default: #Grid1.

Install the canvas to a new ApplicationModel class named MyCongressGrid or similar. You will need to load the package/parcel "Grid Samples" to obtain data to fill this example. Define the #postBuildWith: for this application class as

postBuildWith: aUIBuilder
      | grid rows |
      super postBuildWith: aUIBuilder.
      grid := self widgetAt: #Grid1.
      rows := CongressTable longExample members asList
        collect: [:each | GridRow on: each height: 25].
      grid rows: rows.
      "Add column header"
      grid addRow: ((GridHeaderRow new)
        height: 25;
        yourself)
        beforeIndex: 1

Be sure to place the file congress.str in the image directory of your VW installation. This file will be read to fill the table. Open the sample Grid application and enjoy.


VisualWorks 7.7   Revised September 30, 2009