This program was motivated by an email exchange with a hobbyist programmer
involving two programs that
were giving him trouble. I'm not sure of George's age, but in one of his replies
he said "You have made a
Great Granddad very happy", so he must be of the up there with me as part of the
"old fogey" generation. We need all the help we can get!
The first problem has two parts. For one, he wanted the cells of a StringGrid to
change as the mouse cursor
passed over them. Initially the objective was to highlight the cell by coloring
the background. This is
implemented as "StringGrid Demo 1". The secondary objective was to change the
text in the cell when the
mouse cursor was present and restore the original text when the mouse leaves.
This is "StringGrid Demo 2".
The second problem, was actually George's first request, how to successfully
transfer an image, or portion of
an image canvas, to a bitmap for further processing. The problem was that he did
not realize that the Width
and Height properties of a bitmap must be specified before attempting to transfer to it. I
decided to illustrate this, and
have a little fun, by transferring cell-sized pieces of a Bitmap image to the
StringGrid as the mouse moves over
cells. This is "StringGrid Demo 3" with two options: the "Simple" one just
"paints" the bitmap to the
StringGrid cell by cell as the cursor moves over it. The "Tricky" option places
images on empty cells as the
mouse passes over them, but erases any cell touched by the cursor if the image
piece is already displayed.
Zigzagging across columns and down the rows makes this almost too easy, so the
program randomly paints a
few cells initially just to make it a little more interesting.
All the demos use different versions of the same two Event Exits: "OnMouseMove" gets called
when the mouse cursor is on the
StringGrid and moves. The exit receives the screen coordinates of the cursor but
StringGrid objects include a
"MouseToCell" method that converts (X, Y) screen coordinates to
and Row values. The specific action
taken varies with the demo, but the key is that any change made to a cell will
trigger a call to the "OnDrawCell"
event exit where the appropriate action can be taken to change what the user
StringGrid Demo 1: Highlight cell under mouse cursor
Redraws the entire StringGrid on every
OnMouseMove exit simply converts screen coordinate to cell
coordinates and saves them in global variables that OnDrawCell can
check. Since no cell data is changed, MouseMove calls the grid's
"Invalidate" method to force all cells to be redrawn. The DrawCell exit is called once for each cell to be redrawn. This exit
checks the saved cell coordinates against those of the cell to be
redrawn and when they match, the exit colors the background as
desired. All other cells get the default (white) background color.
All three StringGrids in this program have their "DefaultDrawing"
property set to False when they were defined.. This lets the
DrawCell exits completely control Cell appearance. All other
properties are at their initial default values.
A better solution would be to have the Mouse move routine save x,y
at end of each entry (as previous location) and compare new x,y
with previous x,y and "Update" rather than "Invalidate" the
to redraw only the changed cells. String Demo 2 implements this
StringGrid Demo 2: Change text and redraw only the cells that change as
This version also modifies the text (to '!!!!') in the cell currently under
the mouse and restores it to the initial value when the mouse leaves the cell.
On entry, MouseMove saves the current cell location in variables
NewCol and NewRow. It then checks to see if:
- the cursor is on a cell since area covered by the cells maybe less than
- The current cursor location is different from the previous location
(saved in SaveCol and SaveRow). Since MouseMove is
called for every detected movement, multiple calls may be made as the cursor
moves inside the cell. We want only to process the first entry
in this cell.
If 1) and 2) are true, then:
- If SaveCol has a valid value (i.e. this is not the
first entry), we will restore a previously saved SaveCell value into
the cell. SaveCol is initially set to -1 in a
PageControlChange exit indicating to the other methods that there is no
previous value to restore.
- Move Newcol & NewRow values to SaveCol & SaveRow
and save the current cell value in SaveCell so that it can be
restored when the Mouse leaves this cell.
Since we are now changing Cell values rather just the background color, we
can call the grid's "Update" method which will redraw only changed cells
rather than calling "Invalidate" which forces all cells to be withdrawn.
StringGrid Demo 3: Create a bitmap and "paint" image pieces on the grid.
Bitmap manipulation was another problem George had because he did not realize
that bitmap Width and Height properties must be set before copying images to them. This
grid demo version starts with a default image or one you load into a
TImage. Bitmap B1 has been resized so that it's size
matches the size of the StringGrid. The bitmap of the image is then drawn to the
B1 Canvas using the StretchDraw method.
When the mouse moves over the grid this time, the portion of the B1 relative
to that cell is copied to the grid. The "Tricky" paint version primes the
grid with a few cell images and then MouseMove toggles images on and off each time the
mouse enters the cell. A little experimentation will train you to plug the
"holes" left from your first attempts and complete the image.
The "Change image" button allow the user to choose a replacement image
for the default image. Note that images will be stretched to fit the square
StringGrid dimensions regardless of their original dimensions. Also, note that the
program uses the Delphi JPEG component to load ".jpg" files as
well as ".bmp" bitmap files.