t e m p o r a l 
 d o o r w a y 

Making A Cell Editor For TStringGrid

 

Introduction

Sometimes you want to edit the content of a TStringGrid. When you do, if the default editor is not sufficient, you must create your own editing system for the string grid. This document helps with the most difficult case - where your editor needs to be a combo box.

Note that this technique can also work for a TDrawGrid, from which TStringGrid is derived.

Getting Started

You need to first derive a class from TStringGrid which will be your class. In this case, I will call the example derived class AStringGridWithCombo;

In this example, the grid has two columns. The first column is fixed and cannot be edited. The second column can have a string value based on the contents of a TStringList property of the grid called myListOfStrings.

The editor is a TComboBox which is loaded with those values each time it is made visible by a click on the grid.

Implementation Concepts

The implementation relies on overriding the Click function. Click is the function of TStringGrid (and many other controls) which calls the OnClick event handler. In this case, we do not ever call an OnClick handler, however, we simply capture the event for our own use - and in this case, that use is to create and display our editor.

The editor cannot be made a child of the grid - it must be a child of the form. This is because when it is a child of the grid, the combo is clipped to the boundary of the currently selected cell, and the drop down is therefore invisible. Such a restriction may not be a problem for editors which are derived from TEdit, and which would not mind such clipping. At any rate, when the editor is not a child of the grid, some calculation must be performed to reposition the editor over the proper cell and make it the proper size to appear to be in the cell.

The editor in this example, being a combo, uses the OnChange event to accept the result, insert it in the cell, and make the editor invisible again. Note that the editor is only created the first time it is needed, and then it is kept (invisible), until the control is destroyed.

A TEdit control would need to detect a return key or some other signal to deal with this.

Also note that the combo is shown dropped down. Thus, a change of focus to another control is considered a change, and the editor properly disappears. If this were not the case, you might have to have an OnExit handler for your editor which would perform the function of making the editor invisible again.

The Key Functions

The following are the key functions used in this implementation...

void __fastcall AStringGridWithCombo::Click(void)
{
   if (myEditor == NULL)
   {
      myEditor = new TComboBox(Owner);
      myEditor->Name = Name + AnsiString("Editor");
      myEditor->Parent = Parent;
      myEditor->Visible = FALSE;
      myEditor->Style = csDropDown;
      myEditor->DropDownCount = 30;
   };

   myEditor->OnChange = NULL;

   myEditor->Items->Clear();
   myEditor->Items->Add(" ");
   myEditor->Items->AddStrings(myListOfValidStrings);

   myEditor->OnChange = FieldPicked;

   MoveEditor();

   if (Cells[1][Row] != " ") myEditor->Items->Add(Cells[1][Row]); // Show current value
   myEditor->ItemIndex = myEditor->Items->IndexOf(Cells[1][Row]);

   myEditor->DroppedDown = TRUE;
}
//---------------------------------------------------------------------------
void __fastcall AStringGridWithCombo::TopLeftChanged(void)
{
   Invalidate();
   MoveEditor();
}
//---------------------------------------------------------------------------
void __fastcall AStringGridWithCombo::MoveEditor(void)
{
   if (myEditor == NULL) return;

   TRect   Rect = CellRect(Col,Row);
   
   myEditor->Top = Top;
   myEditor->Left = Left;

   myEditor->Visible = FALSE;
   myEditor->Top = myEditor->Top + Rect.Top + GridLineWidth;
   myEditor->Left = myEditor->Left + Rect.Left + GridLineWidth;
   myEditor->Height = (Rect.Bottom - Rect.Top) + 1;
   myEditor->Width = (Rect.Right - Rect.Left) + 1;
   myEditor->Visible = TRUE;
}

Here is the event handler for the OnChange event of the editor combo:

void __fastcall AStringGridWithCombo::FieldPicked(TObject *Sender)
{
   Cells[1][Row] = myEditor->Items->Strings[myEditor->ItemIndex];
   myEditor->Visible = FALSE;
}

That function assigns the value picked in the combo to the grid cell and makes the editor invisible. Again, a slightly different strategy might be needed for an editor descended from TEdit.

Conclusion

Hopefully this helps make it clear how to create an editor for a TStringGrid. It isn't easy, but once done, never needs to be done again.

Copyright © 2004 by Mark Cashman (unless otherwise indicated), All Rights Reserved