Recent

Author Topic: Case of the Disappearing icon written to a StringGrid  (Read 745 times)

dgrhoads

  • New Member
  • *
  • Posts: 25
Case of the Disappearing icon written to a StringGrid
« on: April 05, 2021, 06:48:44 am »
I am able to successfully draw an icon into a StringGrid.  However when I try to write a second one in the row below, the first one disappears.  No matter how many I write, the only remaining one is the last one written.

Could someone please explain what is happening and how to fix it.

Code to illustrate this problem is attached.  It includes a folder full of icons.  You will likely need to redefine the path to that folder.

bytebites

  • Sr. Member
  • ****
  • Posts: 394
Re: Case of the Disappearing icon written to a StringGrid
« Reply #1 on: April 05, 2021, 09:37:08 am »
Code: Pascal  [Select][+][-]
  1. if (aCol = imageCol) and (images.Count>0) and (aRow = rc)then begin

aRow = rc is not needed. The drawcell-method should paint all rows and not only the last one.

Zoran

  • Hero Member
  • *****
  • Posts: 1667
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Case of the Disappearing icon written to a StringGrid
« Reply #2 on: April 05, 2021, 11:00:50 am »
The drawcell-method should paint all rows and not only the last one.

True.

I'm attaching the working project to this post. See comments in code.

wp

  • Hero Member
  • *****
  • Posts: 8400
Re: Case of the Disappearing icon written to a StringGrid
« Reply #3 on: April 05, 2021, 11:24:06 am »
Code: Pascal  [Select][+][-]
  1. if (aCol = imageCol) and (images.Count>0) and (aRow = rc)then begin
This is not correct either because it will paint the same image in all rows. you must check against a valid number being in the index column and use this to extract the corresponding image from the image list.

See my attached project

P.S. Zoran was faster...
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

dgrhoads

  • New Member
  • *
  • Posts: 25
Re: Case of the Disappearing icon written to a StringGrid
« Reply #4 on: April 05, 2021, 01:16:24 pm »
Thank you very much.  That works!!

Two takeaways:
- StringGrid1DrawCell attempts to rewrite all the cells in the grid every cycle.  That is needed to refresh the icons.
- If all the cells were not refreshed every cycle, only the last icon would be visible.

Clearly this is part of the hidden magic of the LCL.

wp

  • Hero Member
  • *****
  • Posts: 8400
Re: Case of the Disappearing icon written to a StringGrid
« Reply #5 on: April 05, 2021, 02:30:17 pm »
- StringGrid1DrawCell attempts to rewrite all the cells in the grid every cycle. 
No. There are also methods such as InvalidateCell, InvalidateCol, InvalidateRow, InvalidateRange which repaint only part of the grid.

- If all the cells were not refreshed every cycle, only the last icon would be visible.
When this happens something is wrong in your code. The OnDrawCell code in my example above (slightly modified) should be correct, though:
Code: Pascal  [Select][+][-]
  1. procedure TfrmIcon.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  2.   aRect: TRect; aState: TGridDrawState);
  3. VAR
  4.   bRect : TRect;
  5.   w, h: Integer;
  6.   stgCanvas: TCanvas;
  7.   imgIdx: Integer;
  8. begin
  9.   if (aCol = imageCol) and (images.Count>0) then begin
  10.     // Read image index from "index" column and make sure that there is an image for it in the imagelist
  11.     if not TryStrToInt(StringGrid1.Cells[indexCol, ARow], imgIdx) or (imgIdx >= Images.Count) or (imgIdx < 0) then
  12.       exit;
  13.     bRect := ARect;
  14.     InflateRect(bRect, -2, -2);
  15.     // Keep aspect ratio of image
  16.     if bRect.Width > bRect.Height then
  17.     begin
  18.       h := bRect.Height;
  19.       w := round(images.Width / images.Height * h);
  20.     end else
  21.     begin
  22.       w := bRect.Width;
  23.       h := round(images.Height / images.Width * w);
  24.     end;
  25.     // Center image horizontally ...
  26.     bRect.Left := (bRect.Left + bRect.Right - w) div 2;
  27.     bRect.Right := bRect.Left + w;
  28.     // ... and vertically
  29.     bRect.Top := (bRect.Top + bRect.Bottom - h) div 2;
  30.     bRect.Bottom := bRect.Top + h;
  31.     // Paint image into destination rectangle
  32.     stgCanvas := StringGrid1.Canvas;
  33.     images.StretchDraw(stgCanvas, imgIdx, bRect);
  34.   end;
  35. end;
« Last Edit: April 05, 2021, 02:33:18 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

dgrhoads

  • New Member
  • *
  • Posts: 25
Re: Case of the Disappearing icon written to a StringGrid
« Reply #6 on: April 05, 2021, 07:00:50 pm »
Quote
When this happens something is wrong in your code. The OnDrawCell code in my example above (slightly modified) should be correct, though:

I appreciate the efforts you spend on behalf of newbies such as myself.  I am working hard to try to understand the basic processes.  I find that I learn a great deal from all you hero members.

The basic modification that you suggested to my code was to change the gate conditions so that all rows were refreshed each cycle.  In that case, all the previously placed images were visible.  Prior to doing that, the gate conditions were such that only one cell was refreshed each cycle.  In that case, only the icon in that one cell was visible.  It seems to me that there must be some process which is clearing images from cells so that they need refreshing each cycle.  Is that true?

As I understand it, the placement of icons in a StringGrid cell is that it writes the image into a rectangle on the screen which is carefully calculated to be the location of that cell.  Unlike strings, there is no permanent attachment of the image to the StringGrid.  Is that correct?

wp

  • Hero Member
  • *****
  • Posts: 8400
Re: Case of the Disappearing icon written to a StringGrid
« Reply #7 on: April 05, 2021, 10:25:17 pm »
I get the impression that you do not understand how the grid does the painting. It iterates through all cells to be painted and calls the method DrawCell for each cell. In this methods it checks whether an OnDrawCell event handler is provided by user code. When this is false the cell is painted in its default way by calling the method DefaultDrawCell. When there IS an OnDrawCell handler, the cell is painted by the event handler, i.e. by your code. But there is also the property DefaultDrawing: when this is true the DefaultDrawCell method is called even in this case, but before the onDrawCell event. Having DefaultDrawing = true you can let the grid do all its default painting, but you can interfere and force it to paint a few selected cells in your own way.

Code: Pascal  [Select][+][-]
  1. procedure TCustomDrawGrid.DrawCell(aCol,aRow: Integer; aRect: TRect;
  2.   aState:TGridDrawState);
  3. var
  4.   OldDefaultDrawing: boolean;
  5. begin
  6.   if Assigned(OnDrawCell) and not(CsDesigning in ComponentState) then begin
  7.     PrepareCanvas(aCol, aRow, aState);
  8.     if DefaultDrawing then
  9.       DefaultDrawCell(aCol, aRow, aRect, aState);
  10.     OnDrawCell(Self,aCol,aRow,aRect,aState)
  11.   end else begin
  12.     OldDefaultDrawing:=FDefaultDrawing;
  13.     FDefaultDrawing:=True;
  14.     try
  15.       PrepareCanvas(aCol, aRow, aState);
  16.     finally
  17.       FDefaultDrawing:=OldDefaultDrawing;
  18.     end;
  19.     DefaultDrawCell(aCol,aRow,aRect,aState);
  20.   end;
  21.   DrawCellGrid(aCol,aRow,aRect,aState);
  22. end;

In your case, you only want to paint cells in the "Image" column - therefore, the first check must be whether the currently painted column (ACol) is the image column. In this column you want to paint every cell - therefore, there is no need to check ARow, the currently painted row index. All the cells in the image column are supposed to display the image which is collected in your imagelist, and the image index is listed in the "Index" column. Therefore, we take the contents of the "Index" column (Grid.Cells[IndexCol, ARow]) and convert it to an integer (Grid.Cells always is a string); finally we lookup the image in the image list under this index and stretch-draw it onto the grid's canvas.

As I understand it, the placement of icons in a StringGrid cell is that it writes the image into a rectangle on the screen which is carefully calculated to be the location of that cell.  Unlike strings, there is no permanent attachment of the image to the StringGrid.  Is that correct?
Yes, but even the strings do not stick to the grid permanently. When you scroll the grid by a few pixels it looks as if the strings are moving with the grid, but this happens only because the grid is repainted whenever something affecting its appearance changes.
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

dgrhoads

  • New Member
  • *
  • Posts: 25
Re: Case of the Disappearing icon written to a StringGrid
« Reply #8 on: April 06, 2021, 09:35:31 pm »
Thank you for the clarification. 

Also, thank you for your suggestions on how to better display the images in the StringGrid.  It is a big improvement over what I had previously.

 

TinyPortal © 2005-2018