1

In Delphi, how can I create a class derived from the TStringGrid class such that the TObject array associated with the grids cells is of a more specific type for example TColor which would be used to specify the colour of the cell?

Tony Wolff
  • 732
  • 1
  • 10
  • 23
  • You might want to observe -- given the wording "a more specific type" -- that the Delphi `TColor` type isn't a class and so doesn't belong to any class hierarchy. A `TColor` isn't a `TObject`. It so happens, however, that a `TColor` is a 32-bit integer and therefore fits into a `TObject` (=pointer) variable on all platforms used today (32 bit and 64 bit). `TButton`, `TBitmap`, and `TStringList` *are* classes, however, so a `TStringList` instance *is* a `TObject`. – Andreas Rejbrand Feb 05 '21 at 10:21

2 Answers2

6
type
  TStringColorGrid = class(TStringGrid)
  private
    function GetColor(ACol, ARow: Integer): TColor;
    procedure SetColor(ACol, ARow: Integer; AValue: TColor);
  public
    property Colors[ACol, ARow: Integer]: TColor read GetColor write SetColor;
  end;

function TStringColorGrid.GetColor(ACol, ARow: Integer): TColor;
begin
  Result := TColor(inherited Objects[ACol, ARow]);
end;

procedure TStringColorGrid.SetColor(ACol, ARow: Integer; AValue: TColor);
begin
  inherited Objects[ACol, ARow] := TObject(AValue);
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • As I said in my answer, that is only correct for data that has the same size as a pointer. The OP said TColor was just an example. – fpiette Feb 05 '21 at 09:24
  • 5
    @fpiette it will work fine for types *up to* the size of a pointer, they can be smaller. On a 32bit system, Int64 won't work, for instance. Also, extra care has to be taken for reference-counted types, like strings and interfaces. But this is just one example of how to implement a custom property to work with a different type than TObject. – Remy Lebeau Feb 05 '21 at 09:31
2

TStringGrid can hold a TObject for each cell. TColor doesn't inherit from TObject so it doesn't work.

You could cast a TColor to a TObject but this would be a bad solution prone to future issues. And this would not work for any type (Only those having at most the size of a pointer).

The best solution is to "box" your data into a TObject and save the instance of such an object into the StringGrid.

TMyBoxingColorObject = class
    Data : TColor;           // Or any other datatype
end;

Don't forget to create and free the object as needed!

You can also use generics if you have a lot of different types to box.

fpiette
  • 11,983
  • 1
  • 24
  • 46
  • 1
    "*prone to future issues*" - only if Delphi ever makes `TObject` use ARC again. Which given Embarcadero's backpeddling on that feature in 10.4, is unlikely to happen again anytime soon, if ever. "*The best solution is to "box" your data*" - the problem with boxing is ownership. Who owns the box objects and should free them? `TStringGrid` is not virtual enough to do that reliably solely in a descendant, putting burden on the user, and defeating the purpose of making a descendant at all. – Remy Lebeau Feb 05 '21 at 08:15
  • I'd say it is fairly idiomatic in Delphi to use `Tag`, (list view item) `Data`, `Objects[i]` etc. to store anything that fits in a native-sized integer. (Also, saying that "TColor doesn't inherit from TObject" is a bit misleading, since TColor doesn't inherit from anything.) Still, your suggestion is great if you need to store more data than you can fit in a native-sized integer. – Andreas Rejbrand Feb 05 '21 at 08:20
  • I am hoping that once created, the following grid.Objects[c,r]. would "know" that I am referring to an object of type TColor and that when I type the fullstop I get options associated with TColor. Is that the expected behaviour? – Tony Wolff Feb 05 '21 at 08:59
  • @TonyWolff no, that is not how things work – Remy Lebeau Feb 05 '21 at 09:26
  • Tony, if you want that, without using a cast, you have to derive a new class from TStringGrid (Or custom one) and expose a new property of the proper boxing type. Personally I use boxing class with success and use a cast: TMyBoxingColorObject(MyStringGrid.Objects[Col, Row]). Then if you add a dot, Delphi show the available items. Depending on your taste, an interposer class could be helpful as well. – fpiette Feb 05 '21 at 09:49
  • @fpiette the FMX StringGrind does not have an Objects property. How do you di this in FMX? – Michael Riley - AKA Gunny Apr 21 '23 at 11:11
  • @MichaelRiley-AKAGunny This is out of scope of the original question. Feel free to ask for a new specific question. That's how StackOverflow works. – fpiette Apr 21 '23 at 13:59