Recent

Author Topic: grid question... making newly added cell text extend through the cells to right  (Read 10415 times)

swoop80

  • New Member
  • *
  • Posts: 40
I am learning about Lazarus and grid programming.  I read something that seemed to say that if I manipulated Right side of the RECT, I could make the newly entered text in that cell extend to the right (showing up in the cells that are just to the right of the text-affected cell).... like what occurs in Excel.

Can you help me figure out how to do that?

Note: I have already realized that I have to change TextOut to TextRect... but so far, I haven't gotten it working.  Wondering if I may have to do something to first EMPTY the adjacent cells to the right... and maybe make them "invalidated".  Appreciate your help.

wp

  • Hero Member
  • *****
  • Posts: 13353
"TextOverflow" into the next cell is quite complex. It's not just painting the text into a larger rectangle, you must also determine how many adjacent cells are available. Therefore, you must create a new grid class in which you have access to the DrawRow method. Plus maybe some more which I forgot.

Have a look at the TsWorksheetGrid of the fprspreadsheet package. It implements the TextOverflow feature as well as merging of cells. See http://wiki.lazarus.freepascal.org/TsWorksheetGrid and http://wiki.lazarus.freepascal.org/FPSpreadsheet

swoop80

  • New Member
  • *
  • Posts: 40
Thanks WP.  I wonder if I can use ONLY the 'merge cells' solution.   Or - do I need to try to use the two things you mentioned TOGETHER?

wp

  • Hero Member
  • *****
  • Posts: 13353
Certainly. The Merge is independent of the TextOverflow. But as I said I am almost sure that it is not possible to implement it with the events exposed by TStringGrid, you'll also need access to protected methods of the grid.

You must activate the Grid Option goColSpanning and then you must override the method CalcCellExtent in which you define the cell rectangle of the entire merged block. But you must also hook into other protected methods:

- DoEditorShow: in order to make the cell editor the same size as the merged block
- DrawSelection: draw the selection to be as large as the merged block
- GetEditText: get the text for editing from one of the merged cells. Dto with SetEditText
- DrawRow: prevent drawing the individual cells of the merged block (maybe handled by calling CalcCellExtent, I can't remember).
- HeaderSizing: enforce repainting the entire merged block if the width/heigth of one of the merged cells is changed by the mouse

Maybe there are more, I forgot the details.

swoop80

  • New Member
  • *
  • Posts: 40
WP, you said:

You must activate the Grid Option goColSpanning and then you must override the method CalcCellExtent in which you define the cell rectangle of the entire merged block. But you must also hook into other protected methods:

- DoEditorShow: in order to make the cell editor the same size as the merged block
- DrawSelection: draw the selection to be as large as the merged block
- GetEditText: get the text for editing from one of the merged cells. Dto with SetEditText
- DrawRow: prevent drawing the individual cells of the merged block (maybe handled by calling CalcCellExtent, I can't remember).
- HeaderSizing: enforce repainting the entire merged block if the width/heigth of one of the merged cells is changed by the mouse


Well, I figured out how to do the first setting (goColSpanning)...

Can't figure out this: override the method CalcCellExtent      (honestly, I haven't found much info on it yet, but what I have found seems to deal with Custom grids)...

« Last Edit: October 03, 2016, 01:09:26 am by swoop80 »

wp

  • Hero Member
  • *****
  • Posts: 13353
You said you are new to Lazarus, so maybe that what follows will be too much for you. Here are the basics:

Define a new class which inherits from TStringGrid (or TCustomStringGrid - but that's another detail...). In the protected part of the class declaration declare the procedure CalcCellExtent and add the attribute "override" to indicate the compiler to use this new method instead of the inherited one. In the new CalcCellExtent method you must tell the grid which cells belong to the merged block, i.e. you return the left, top, right and bottom coordinates of the block. The most flexible way to do this is by introducing an event - let's call it OnMergeCells: this event will get the col/row parameters of a cell - this cell is checked if it belongs to a merged block. If it does the event will return the outer coordinates of the entire block. The CalcCellExtent method will call this event and calculate the rectangle parameters to be passed to the calling function.

The next major task is to paint the cell text into to larger merged block. All the painting jobs in the grid are performed by little virtual methods, therefore you can almost completely control the entire painting process. The most suitable method for our task is the DrawCellText method. When this is called all the background and the grid lines already have been painted. You must override this and increase the drawing rectangle to that of the merged block, and you also must determine where the cell text comes from. I propose to write the cell text into the upper left cell of the merged block. Therefore, DrawCellText must look for this cell and pick the text from it.

I wrote a little demo of these basic steps. Just run the attached project. There are still lots of features missing: you can select every individual cell of the block, text input into the merged block is strange (enter it into the top left cell of the block, and then enforce a repaint of the entire grid, e.g. by dragging the window out of the monitor and back in again).

To fix all that you must find the corresponding method of TCustomGrid or TCustomDrawGrid and modify it in the code of TStringGridEx to your needs. Have a look at the list that I had mentioned in the previous post.

When everything is perfect you can create a package and install your new component into Lazarus. The demo uses only a runtime-created version. In the end you will have made a good learning experience of the inner workings of the LCL.

swoop80

  • New Member
  • *
  • Posts: 40
Whoa.  I will definitely be studying for a while for this!   Haha.   Nice.  Thanks for taking this time to teach, WP.  Will get back later.

wp

  • Hero Member
  • *****
  • Posts: 13353
Since this task is not quite easy for a beginner I added most of the missing code to the demo. Now the extended stringgrid behaves much more convenient. See the new attachment.

wp

  • Hero Member
  • *****
  • Posts: 13353
To complete I added an event OnDrawTextCell which can be used to easily hook into the text painting process (not like OnDrawCell where the entire cell needs to be custom-drawn). This is good for vertical text, word-break within cells, center/right-aligned output etc. See the attached demo.

I uploaded this demo also to the examples/gridexamples/merged_cells folder of lazarus trunk.

swoop80

  • New Member
  • *
  • Posts: 40
Hi WP.  I've been somewhat swamped but am back at this now.  I downloaded your most recent project in this thread and thought I'd look at it some more.  I tried to compile it and run it, but got this error: Project mergedcells_project raised exception class 'External; SIGFPE'.  In file 'lclproc.pas' at line 861.

wp

  • Hero Member
  • *****
  • Posts: 13353
No idea what's wrong. I tested with various combinations of Lazarus (trunk, 1.6, 1.44, 1.2, 1.0) and fpc (trunk, 3.0, 2.6.4, 2.6.2, 2.6.0) on Windows and Linux and never saw such an error. Did you modify something in the code? What is your operating system? Which versions of Lazarus and FPC?

swoop80

  • New Member
  • *
  • Posts: 40
No modifications at all.  I run Windows 8.1 on my laptop.  I decided to go to the version you posted in the prior post and give that one a try - it worked great.  I will work with that one.

I clearly have a lot to learn, WP.  See, I took an earlier version of your code and tried to integrate into the code I previously had... that failed miserably, and I don't understand the errors I'm getting from that attempt.

I'm reading more now, and then may try going the "other direction" (where I take what I DID know how to do based on my experiments, and try to put that into a copy of what you provided... instead of the other way around).


swoop80

  • New Member
  • *
  • Posts: 40
A big difference in what you have vs what I had before was this:  in my old project, the grid was not developed dynamically... so, I am not sure what I'll need to do to the dynamic one to get some of the features i had before working.   (I'm sure there is a way to do it, and I'll nose around on the internet to try to figure it out.)

wp

  • Hero Member
  • *****
  • Posts: 13353
You mean the merged_cells files? Since I wanted to re-use the example in the Lazarus examples folder I renamed the grid to TMCStringGrid (instead of TStringGridEx of the first example; "MC" is short for "merged cells"). And all the grid-related code is in a separate unit, mcgrid, now. (It is almost a separate component now, you'd just have to add the unit to a package and install into the IDE). So, these are the steps to apply the new grid in a new project
  • Copy the mcgrid.pas file in you project file
  • Add "uses mcgrid" to the interface uses clause of the forms into which the grid will be inserted
  • Add the name of the grid and its type (in my example: "Grid: TMCStringGrid") to the private section of the form
  • Add this code to the OnCreate handler of the form:
    • Create the grid: "Grid := TMCStringGrid.Create(self);"
    • Define its parent (i.e., the control into which it is inserted), in my code this is the form (called "self" in the most general way): "Grid.parent := Self;"
    • Position the grid by defining its Left, Top, Width and Height properties, or by using the Align property. Align = alClient, used in the example, means that the grid fills the entire form.
    • Write an event handler for the OnMergeCells event of the grid. This is needed to tell the grid which cells will be merged. In the example, the cells covered by columns 2, 3 and rows 3,4,5 will be merged. Therefore, say "if (ACol in [2..3]) and (ARow in [3..5]) then begin ALeft := 2; ARight := 3; ATop := 3; ABottom := 6; end". Instead of "ARow in [3..5]" you can also write "if (ARow >= 3) and (ARow <= 5)". In my example, the event handler procedure is called "MergeCellsHandler"
    • Assign this procedure to the OnMergeCells event of the grid: "Grid.OnMergeCells := @MergeCellsHandler" (Note: in Delphi compiler mode you must drop the @ character).
    • To activate merging add goColSpanning to the Grid.Options.
That's basically all for merging. The last example introduces another event, OnDrawCellText, which you can use to activate wordwrap, right-align or center or rotate the text. Seek in the wiki for the related canvas drawing procedures. (Or ask again here). (You could also use the event OnDrawCell, but the new event saves some steps).

swoop80

  • New Member
  • *
  • Posts: 40
I just meant that I finally got some Grid stuff to work in another example project, but that project had attributes of the grid defined at design time - not at execution time.  Thank you again.  It's pretty impressive.

Note: that exception that I mentioned in the above post?   Well, that same issue came out even in the other downloaded project (colspanning2).   But this time it happened after I had done a very small change. 

Thanks again - I can only spend some time here and there, but I'm determined to learn more of this stuff.

 

TinyPortal © 2005-2018