Forum > LCL

How to install a new property on a TGridColumn component?

(1/3) > >>

Al3:
How do I add new properties to the columns of a TStringGrid, which are created dynamically?

I want to create a "datatype" property to each column object so that each column has its own virtual type instead of just displaying strings, but I am new to the languages and I only found how to install properties on a TForm.

Handoko:
That will require too much work. Basically, you can add a new property to a class but in this case you're trying add a property to a class that used by TStringGrid. Simply adding some code for TGridColumn, which is the class that handle column in the grid is not enough. You need to modify the code for TGridColumns and then TStringGrid.

So I think you need to copy/paste to code of TStringGrid, TGridColumns and TGridColumn to a new module or file and make necessary changes by inheriting them to newer classes. And they should be named differently, maybe TStringGridEx, TGridColumnsEx and TGridColumnEx.

Perhaps the easiest is to write a child class inherited from TStringGrid, add a new ColumnEx property and write necessary code on how ColumnEx will affect the data to show on the Paint and/or PrepareCanvas calls. This solution is less integrated into all the string grid internal functions.

wp:
I would not use a TStringGrid, but a TDrawGrid. The difference between them is that the latter does not store any data. This way you can freely choose the data type per column (in TStringGrid, there's no way to get around the strings...).

The first task is how to extend the DrawGrid such that its columns have a new property DataType. Define a new class, e.g. TDataGrid:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  TDataGrid = class(TDrawGrid)  ...
Its basic ancestor, TCustomGrid, has a method CreateColumns which creates the columns of the grid as type TGridColumns. Being a TCollection, the TGridColumns can specify the type of the items in the collection in the constructor:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---function TDataGrid.CreateColumns: TGridColumns;begin  Result := TGridColumns.Create(self, TDataGridColumn);end;
The original code of TCustomGrid has TGridColumn here as last parameter, we replace it by a descendant TDataGridColumn and thus are able to create the column items as instances of a new class with a new property, DataType:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  TDataType = (dtString, dtInteger, dtFloat, dtDate);  // change as needed   TDataGridColum = class(TGridColumn)  private    FDataType: TDataType;  published    property DataType: TDataType read FDataType write FDataType;  end;
Now, the new TDataGrid contains columns with a DataType property.

But that's only the easiest part of the story...

Inherited from TDrawGrid, we must enable the new grid to draw its cell content. The pre-requisite for this is that the grid "knows" how to find the data. Are they in a 2D-Array of variant? Or are they an array of records in which each record contains the data types defined by the new columns? Is the data storage independed of the grid, or part of it? Then you must override the DefaultDrawCell method which which must extract the cell value from the data storage for the col/row index provided, and draw it. You must override the SetEditText and GetEditText methods to invoke the cell editor for entering or editing the cell values. And probably a lot more...

If you look at the code of the TsWorksheetGrid of the fpspreadsheet package (https://wiki.lazarus.freepascal.org/FPSpreadsheet, https://wiki.lazarus.freepascal.org/FPSpreadsheet) you can find an example how this could be done. BTW: it started small like in above basic description, but now has grown to a monster of more than 7000 lines...

It's a good exercise in component writing, but definitely not the best starting point for a novice.

Al3:
I figured it has to be something harder than usual.
It of course wouldn't worth the effort and I should probably find a different way to do that such as associating the column object with a set or something?
In CGTK component objects can be associated with arbitrary data which internally uses a hash table.
I wouldn't learn how to write components just when I started using OP again  :D
Also while TStringGrid displays string cells, I should be able to easily (re)format them based on the column data type once I find out how to associate it.
Another probably simpler, but less convenient way is to just grow an array dynamically depending on the number of columns and store data there.
Of course I have 0% confidence in coding as of now, but I think that's what I would've done in C.

Handoko:
There are many possible solutions, writing a child class inherited from TStringGrid, or modify all the column-related classes of TStringGrid, use a TDrawGrid as suggested by wp, even writing a new component from scratch.

Maybe you can draw some sketches showing what you want it to look like and explaining what features you need. So we can suggest you which solution suits your case. I may write it for you if that is not too complicated.

Navigation

[0] Message Index

[#] Next page

Go to full version