1

Is there a way to do a text search in a string grid using a find dialog? I need to find a text and highlight it's background as usually when a text is found.

Thanks!

maxfax
  • 4,281
  • 12
  • 74
  • 120

1 Answers1

8

Like this:

procedure TForm1.FormClick(Sender: TObject);
begin
  FindDialog1.Execute(Handle)
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FindDialog1.Options := [frDown, frHideWholeWord, frHideUpDown];
end;

procedure TForm1.FindDialog1Find(Sender: TObject);
var
  CurX, CurY, GridWidth, GridHeight: integer;
  X, Y: integer;
  TargetText: string;
  CellText: string;
  i: integer;
  GridRect: TGridRect;
begin
  CurX := StringGrid1.Selection.Left + 1;
  CurY := StringGrid1.Selection.Top;
  GridWidth := StringGrid1.ColCount;
  GridHeight := StringGrid1.RowCount;
  Y := CurY;
  X := CurX;
  if frMatchCase in FindDialog1.Options then
    TargetText := FindDialog1.FindText
  else
    TargetText := AnsiLowerCase(FindDialog1.FindText);
  while Y < GridHeight do
  begin
    while X < GridWidth do
    begin
      if frMatchCase in FindDialog1.Options then
        CellText := StringGrid1.Cells[X, Y]
      else
        CellText := AnsiLowerCase(StringGrid1.Cells[X, Y]);
      i := Pos(TargetText, CellText) ;
      if i > 0 then
      begin
        GridRect.Left := X;
        GridRect.Right := X;
        GridRect.Top := Y;
        GridRect.Bottom := Y;
        StringGrid1.Selection := GridRect;
        Exit;
      end;
      inc(X);
    end;
    inc(Y);
    X := StringGrid1.FixedCols;
  end;
end;

This code can easily be extended to support searching backwards ('up'), and you might also want to implement the 'match whole word' feature.

Perhaps you want to select only the matched text, and not the entire cell? Then do

  if i > 0 then
  begin
    GridRect.Left := X;
    GridRect.Right := X;
    GridRect.Top := Y;
    GridRect.Bottom := Y;
    StringGrid1.Selection := GridRect;
    GetParentForm(StringGrid1).SetFocus;
    StringGrid1.SetFocus;
    StringGrid1.EditorMode := true;
    TCustomEdit(StringGrid1.Components[0]).SelStart := i - 1;
    TCustomEdit(StringGrid1.Components[0]).SelLength := length(TargetText);
    Exit;
  end;

instead. But this will steal the focus from the find dialog, and so the user will not be able to press Return to select the next match, which might be annoying.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • 2
    +1 for sneaking a use of `goto` into the code. In production code where you were iterating over grids a lot then you may well write some enumerators that flattened the grid so you could walk it with a `for in` loop. – David Heffernan Aug 10 '11 at 11:03
  • 3
    I like David's enumerator idea. And in production I would be tempted not to use a StringGrid at all, but rather to use a VirtualTreeView control, used as a grid, and to have a model object contain the content of my document, which is displayed in the grid, and which implements a memento/command-pattern and has undo/redo features. Also, I would implement a pony. – Warren P Aug 10 '11 at 15:24
  • One question, what is StringGrid1.Components[0]? :) – maxfax Aug 10 '11 at 18:07
  • 1
    @maxfax: That is the inplace editor. When you edit the text in one of the cells, the editor pops up (it is like a `TEdit` with no border) and is positioned above the cell. Since this editor control is a child of the string grid, and also the *only* child (at the very least, the first one), you can access it via the `Components` array. – Andreas Rejbrand Aug 10 '11 at 18:11
  • +1; it is written that I have no components - list index out of bounds(0). What to add? Thanks. – maxfax Aug 10 '11 at 18:25
  • 1
    @maxfax: There is only an inplace editor if the cells are editable (of course!). You have to have `goEditing in Options`, and the editor has to be created. (It is automatically, since my code above does `EditorMode := true`.) If you don't want the cells to be editable, you'll have to do custom painting to select (highlight) only the sought text, and not the entire cell... – Andreas Rejbrand Aug 10 '11 at 18:37
  • Sorry, goEditing was disabled. Your help is really good. Thanks! – maxfax Aug 10 '11 at 18:45
  • So if GoEditing is enabled how to prevent user's editing (to change a text of cells)? Read Only? – maxfax Aug 10 '11 at 18:47
  • @maxfax: Yes, I guess you could make the inplace editor read-only, but the more tricks you apply, the less robust the code gets...' – Andreas Rejbrand Aug 10 '11 at 18:50
  • Why not `Exit;` instead of `goto TheEnd;` ? – nurettin Jun 29 '18 at 05:54
  • @nurettin: No good reason at all. Most likely I added that `goto` at a time when I had code after the label. But then I removed that code. I'll update the answer. – Andreas Rejbrand Jun 29 '18 at 06:10
  • why not put the code after the label into finally :-P – nurettin Jun 29 '18 at 06:14
  • @nurettin: That makes sense in many cases, especially if you need to protect resources or some state (from exceptions, early exit etc.) but not all. But of course I cannot remember what the code looked like from the beginning, since this A is eight years old! (Also, sometimes I try to "play" a bit and be a bit fun, so it might have been a small humorous aspect to it.) – Andreas Rejbrand Jun 29 '18 at 06:23
  • @AndreasRejbrand By the way, thanks for all the work on algosim, it is very interesting, I tried learning it a few years ago. Interested in seeing the sources, too. Unless you want to make it commercial. – nurettin Jun 29 '18 at 06:29
  • @nurettin: Thank you! Actually, I am currently rewriting AlgoSim from scratch (and have been doing so for a year or two). The new version will be open source! But it won't be released until next year or so, I think. – Andreas Rejbrand Jun 29 '18 at 06:37