Recent

Author Topic: TStringGrid draw lines in empty background space  (Read 1133 times)

Jonny

  • Full Member
  • ***
  • Posts: 104
TStringGrid draw lines in empty background space
« on: January 26, 2025, 03:23:39 am »
Is there a way to draw horizontal lines in the unused background area of a string grid? Kind of like placeholders? So if no rows have been added, it would show the space that they would occupy?

dsiders

  • Hero Member
  • *****
  • Posts: 1350
Re: TStringGrid draw lines in empty background space
« Reply #1 on: January 26, 2025, 04:07:45 am »
Is there a way to draw horizontal lines in the unused background area of a string grid? Kind of like placeholders? So if no rows have been added, it would show the space that they would occupy?

That would be the AlternateColor property.
https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tcustomgrid.alternatecolor.html.
Preview the next Lazarus documentation release at: https://dsiders.gitlab.io/lazdocsnext

Jonny

  • Full Member
  • ***
  • Posts: 104
Re: TStringGrid draw lines in empty background space
« Reply #2 on: January 26, 2025, 02:09:04 pm »
Thanks @dsiders - I should have mentioned that I am already using the AlternateColor property and it works well for existing rows in the grid.

It is the empty space where there are no rows that I would like to "decorate".

Currently that plain space looks ugly and uninviting. If it had lines there then it would appear to be part of the grid.

I have tried adding many additional empty rows but that makes my calculations for used/unused grids tricky - and it adds scrollbars if the sizing is not accurate.

wp

  • Hero Member
  • *****
  • Posts: 12615
Re: TStringGrid draw lines in empty background space
« Reply #3 on: January 26, 2025, 02:17:49 pm »
Drawing "rows" outside the defined grid row space may be misleading to the user because he may think that he could click there which is not possible because there are no cells in this part of the viewport.

If you still need this you'd probably have to subclass the TStringGrid and override the Paint method.

Jonny

  • Full Member
  • ***
  • Posts: 104
Re: TStringGrid draw lines in empty background space
« Reply #4 on: January 26, 2025, 04:30:24 pm »
Quote from: wp
may be misleading to the user

Yeah, I understand, but I am currently rewriting an application in FPC where we have no source and the developer is no longer around nor cooperative It was written with some kind of BASIC where it did paint those lines as default. My clone has to look and behave identically before we start adding features.

The TStringGrid component does not have an OnPaint method and there seems to be no way to access that area.

I am unfamiliar with subclassing and searching shows that I should be doing something like this:

Code: Pascal  [Select][+][-]
  1. type
  2.   TStringGridPaint = class(TStringGrid)
  3.     procedure OnPaint;
  4.   end;
  5.   procedure TStringGridPaint.OnPaint;
  6.   begin
  7.     // paint the background ???
  8.     TStringGridPaint.Canvas.Brush:=bsSolid;
  9.     TStringGridPaint.Canvas.Brush.Color:=clFuchsia;
  10.     TStringGridPaint.Canvas.FillRect(TStringGridPaint.Left,TStringGridPaint.Top,TStringGridPaint.Width,TStringGridPaint.Height);
  11.   end;
  12.  

But cannot find any examples or documentation on creating the rest of the subclass. Is there anything to point me in the right direction?

wp

  • Hero Member
  • *****
  • Posts: 12615
Re: TStringGrid draw lines in empty background space
« Reply #5 on: January 26, 2025, 06:55:31 pm »
A bit advanced topic...

For subclassing you simply declare a new class which descends from TStringGrid. In the code of this new class you have access to all protected methods, among them the Paint method. Here you first call the inherited method (which draws the regular grid). Then you determine whether there's any empty space below the last row. If there is you loop through columns and rows, calculate the rectangle bounaries of each cell and call the inherited method for drawing the cell background (DrawFillRect) and grid lines (DrawCellGrid); these methods already take care of selecting the correct pen and brush for each cell.

Code: Pascal  [Select][+][-]
  1. type
  2.   TNewStringGrid = class(TStringGrid)
  3.   protected
  4.     procedure Paint; override;
  5.   end;
  6.  
  7. procedure TNewStringGrid.Paint;
  8. var
  9.   Rct: TRect;
  10.   y: Integer;
  11.   c, r: Integer;
  12.   state: TGridDrawState;
  13. begin
  14.   // Call the inherited method, i.e. paint the grid as usual
  15.   inherited;
  16.  
  17.   // Measure the rectangle of the last regular row to learn whether there's empty space below it
  18.   Rct := CellRect(0, RowCount-1);
  19.   y := Rct.Bottom;
  20.   if y < ClientHeight then
  21.   begin
  22.     // Yes, we must draw "dummy" rows...
  23.     r := RowCount;
  24.     while y < ClientHeight do begin
  25.       // Draw column by column
  26.       for c := 0 to ColCount - 1 do begin
  27.         if c < FixedCols then
  28.           state := [gdFixed]
  29.         else
  30.           state := [];
  31.         Rct := CellRect(c, RowCount-1);
  32.         Rct.Top := y;
  33.         Rct.Bottom := y + DefaultRowHeight;
  34.         PrepareCanvas(c, r, state);
  35.         DrawFillRect(Canvas, Rct);
  36.         DrawCellGrid(c, r, Rct, state);
  37.       end;
  38.       // Advance to next "dummy" row
  39.       inc(y, DefaultRowHeight);
  40.       inc(r);
  41.     end;
  42.   end;
  43. end;                  

Normally you would have to create this new grid at runtime, or you would have to put it into a package and install it in the IDE to have it on the component palette. But you can even go a step further and name the type of the new grid class like the old one, TStringGrid, and prepend the name of the old grid type by its unit (Grids.TStringGrid).

Code: Pascal  [Select][+][-]
  1. type
  2.   TStringGrid = class(Grids.TStringGrid)
  3.   ...

When you now put the code into the same unit as the form in which the new grid is used, or when you put the code into a separate unit and list that at the end of the uses clause, you can fool the compiler and the IDE: dropping the grid on the form from the component palette at design-time creates an old TStringGrid (Grids.TStringGrid, to be exact) but at run-time the compiler sees only the code of the new stringgrid and calls the new Paint method.

Have a look at the attached demo project.

I still think that this kind of grid is confusing. For example, you can click into one of the dummy cells, but that is not marked as focused, and you cannot type any text in it... Try to convince your boss that this a very poor GUI.

jcmontherock

  • Sr. Member
  • ****
  • Posts: 278
Re: TStringGrid draw lines in empty background space
« Reply #6 on: January 26, 2025, 10:07:16 pm »
What are you thinking about using "Alternatecolor" in TStringrid properties and Rowcount ?
Windows 11 UTF8-64 - Lazarus 4.0RC2-64 - FPC 3.2.2

wp

  • Hero Member
  • *****
  • Posts: 12615
Re: TStringGrid draw lines in empty background space
« Reply #7 on: January 26, 2025, 10:38:13 pm »
My clone has to look and behave identically before we start adding features.
"Behave identically": For the LCL grid you must define the size of the matrix, i.e. the number of columns and rows. There is no way to enter anything outside this defined range. From this point of view, it is just fair that no cells are drawn outside this range. But on the other hand, it could be possible that the application that you have to clone worked like Excel: the size of the grid is quasi-infinite and you can type into any cell that can be reached by scrolling over an extremely large range - Excel supports more than 1 million rows and 16000 columns. TStringGrid would have to reserve memory for this count of rows/columns to behave in the same way, even if you need only a few cells. Excel does not need to do this because it uses a dedicated storage structure for this kind of application.

Jonny

  • Full Member
  • ***
  • Posts: 104
Re: TStringGrid draw lines in empty background space
« Reply #8 on: January 27, 2025, 12:12:10 am »
Quote from: wp
A bit advanced topic...

For subclassing you simply declare

Haha, there is nothing simple about your advanced coding! Thank you again for your clear description and explanation together with the sample code. I have reworked and simplified it to meed my needs (whole rows, no individual cells, minimal draws).

Quote from: wp
Try to convince your boss that this a very poor GUI.

Indeed, it is purely a cosmetic effect. Difficult to convince - I am out of work and making this in order to try and get the job! If I am successful then I can hopefully implement changes.

Here is the updated code:

Code: Pascal  [Select][+][-]
  1. procedure TStringGrid.Paint;
  2. var
  3.   y, r: Integer;
  4. begin
  5.   inherited;
  6.   y := CellRect(0, RowCount-1).Bottom;
  7.   if y < ClientHeight then
  8.   begin
  9.     Canvas.Brush.Color:= AlternateColor;
  10.     r := RowCount;
  11.     if (r mod 2) = 1 then
  12.     begin
  13.       Inc(y, DefaultRowHeight);
  14.       Inc(r);
  15.     end;
  16.     while y < ClientHeight do
  17.     begin
  18.       DrawFillRect(Canvas,rect(0,y,Width,y+DefaultRowHeight));
  19.       Inc(y, DefaultRowHeight*2);
  20.       Inc(r);
  21.     end;
  22.   end;
  23. end;
  24.  

EDIT: optimised the code
« Last Edit: January 27, 2025, 11:55:44 am by Jonny »

wp

  • Hero Member
  • *****
  • Posts: 12615
Re: TStringGrid draw lines in empty background space
« Reply #9 on: January 27, 2025, 10:08:13 am »
Yes, this is better than my proposal in which real and dummy cells could not be distinguished.

 

TinyPortal © 2005-2018