0

I am trying to make use of the Objects property of the Stringgrid inside my descendant and I think I am doing something wrong. So, I created a simple class for use in my StringGrid cells, something like:

type

  TKind = (tkNone,tkStart, tkEnd, tkMiddle);

  TMyCell = class(TObject)
  private
    FKind:string; // TKind: start, middle, end, none
    FOfType:string; // new, registered, paid, over, none
    FActualDate:TDate; 
    FTheText:string; // if you want to show a text in it
    FIsWeekend:Boolean;
    function IsItWeekend(dt:Tdate):boolean;
    procedure setKind(s:string);
    { Private declarations }
  protected
    { Protected declarations }
  public
    constructor Create;
    property Kind:string read FKind write setKind;
    property OfType:string read FOfType write FOfType;
    property ActualDate:TDate read FActualDate write FActualDate;
    property TheText:string read FTheText write FTheText;
    property IsWeekend:Boolean read FIsWeekend write FIsWeekend default false;
    { Public declarations }
  published
    { Published declarations }
  end;


implementation

procedure TMyCell.setKind(s:string);
begin
  FKind:=s;
end;

function TMyCell.IsItWeekend(dt:Tdate):boolean;
begin
  if (DayOfTheWeek(dt)=6) or (DayOfTheWeek(dt)=7) then IsItWeekend:=true else IsItWeekend:=false;    
end;

constructor TMyCell.Create;
var
  i:integer;
  a,l,z:word;
  dt:TDate;
begin
  FKind:='none';
  FOfType:='none';
  FActualDate:=now;
  FTheText:='';
  FIsWeekend:=IsItWeekend(FActualDate);
end;

then, in my StringGrid descendant (TMyGrid), I do the following:

 TMyGrid = class(TStringGrid)
  private
    FStartSelection:integer;
    FFixedColor:TColor;
    FRowCount:integer;
  ...
  published
    property ColCount;
    property RowCount;
  ...

constructor TMyGrid.Create(AOwner: TComponent);
var
  i:integer;
  a,l,z:word;
  dt:TDate;
  j: Integer;
  myCell:TMyCell;
begin
  inherited;
  ...// different settings
  RowCount:=5;
  for i := 0 to colCount-1 do
    for j := 0 to RowCount-1 do
      begin
        Objects[i, j] := TMyCell.Create;
      end;
end;

destructor TMyGrid.Destroy;
var
  i,j:integer;
begin
  for i := 0 to colCount-1 do
    for j := 0 to RowCount-1 do
      begin
        TMyCell(Objects[i, j]).Free;
      end;
  inherited;
end;

... // other stuff

procedure Register;
begin
  RegisterComponents('mygrid', [TMyGrid]);
end;

The problem is I don't know how do I tell my control that there are more rows when the developer changes the RowCount in the objectInspector before running the app. So I drop my StrinGrid descendant on a form, and set the rowCount to 10. But my StringGrid does not have Objects created for the new rows, So the cells on ARow=5->9 do not have objects created... because in OnCreate I only set the initial value of the RowCount to 5 and create objects for i:=0 to RowCount-1.

Is there an event or method where I can tell the StringGrid to create the Objects after the developer changes the rowCount in ObjectInspector?

I am sure that this is my problem because, using the above code, when I drop my stringGrid on a form and set it's rowCount (design time or runtime) to 10 then I want to assign a value to Kind property of a cell that is on a Row>4 I get an AccessViolation, but if I do that for a row that is <= 4 the assignment works just fine.

I found something that should help here: http://embarcadero.newsgroups.archived.at/public.delphi.ide/200904/0904193279.html but I do not know how and where to place this code in my StringGrid descendant class so it would get called when RowCount is changed at designtime/runtime

EDIT

After reading your comments I tried your idea (that seemed to work) to override the SizeChanged (I did not know that method existed, must have skipped it when I serached before). Anyway, I added this code to my class:

TMyGrid = class(TStringGrid)
  private
    ...
    procedure SizeChanged(OldColCount, OldRowCount: Longint); override;
    procedure UpdateGridDimensions(NewColCount, NewRowCount: Integer);
    ...

procedure TMyGrid.SizeChanged(OldColCount, OldRowCount: Longint);
begin
  inherited;
  if (OldRowCount<>FRowCount)or(OldColCount<>ColCount) then
    UpdateGridDimensions(ColCount, FRowCount);
end;


procedure TMyGrid.UpdateGridDimensions(NewColCount, NewRowCount: Integer);
 var
    C, R: Integer;
    Old: Integer;
 begin
    if NewColCount <> ColCount then
    begin
        if NewColCount < ColCount then
        begin
            for R := 0 to RowCount-1 do
            begin
                for C := ColCount-1 downto NewColCount do
                    Objects[C, R].Free;
            end;
        end;

        Old := ColCount;
        ColCount := NewColCount;

        if NewColCount > Old then
        begin
            for R := 0 to RowCount-1 do
            begin
                for C := Old to NewColCount-1 do
                    Objects[C, R] := TMyCell.Create;
            end;
        end;
    end;

    if NewRowCount <> RowCount then
    begin
        if NewRowCount < RowCount then
        begin
            for C := 0 to ColCount-1 do
            begin
                for R := RowCount-1 downto NewRowCount do
                    Objects[C, R].Free;
            end;
        end;

        Old := RowCount;
        RowCount := NewColCount;

        if NewRowCount > Old then
      begin
            for C := 0 to ColCount-1 do
            begin
                for R := Old to NewRowCount-1 do
                    Objects[C, R] := TMyCell.Create;
            end;
        end;
    end;
 end;

but now whenever I drop my control on a form, the rowcount is 93... where do I set that rowCount? Because I DONT.

And still, if I increase the RowCount from 93 to something else like 100, then my Objects exist for the first 93 rows but they do not get created for the 93-100 rows...

So the idea sounded great, but it does not work as I expect it...

Any thoughts? Am I doing it wrong?

user1137313
  • 2,390
  • 9
  • 44
  • 91
  • To detect when you are in designtime mode: [Component Initialization - Runtime vs. Designtime](http://stackoverflow.com/questions/691997/component-initialization-runtime-vs-designtime). – LU RD Jul 30 '15 at 10:32
  • I think you can override dynamic method SizeChanged and initialize grid properly according to new size. – Andrei Galatyn Jul 30 '15 at 10:36
  • 1
    Don't use `Objects[]`. That's for the consumer not the component author. – David Heffernan Jul 30 '15 at 11:01
  • I was trying to give the consumer a component ready to fill with data, and save him from creating new classwes and objects. Especially because the purpose of my coimponent will be a specific one, so the consumer wont need to implement different classes for the cell's object. But I get your point – user1137313 Jul 30 '15 at 11:22
  • This question now is quite a mess. I think you need to get back to basics. There are an awful lot of mistakes here. – David Heffernan Jul 30 '15 at 11:24
  • Sorry for the mess. I am pretty new at this. – user1137313 Jul 30 '15 at 11:27
  • You are attempting something that is beyond your current levels of expertise. That's not meant to be criticism. You need to step back and master something more basic, gain more experience. Then you'll be ready to write a custom grid descendent. – David Heffernan Jul 30 '15 at 11:45
  • I know that. However I learn best by example and by listening to people. I did not learn Delphi in school. I do not have anybody to ask if I have a question. Recently I wanted to know if I could create a new component, since so far I never needed to create custom components. It was enough to get a free one and change it a little so it would fit my needs. Now I want to create this custom StringGrid and the best place for me to learn how to customize it is here. Sorry if I was bothering you. Please ignore me if I offended you with my lack of knowledge. – user1137313 Jul 30 '15 at 12:02
  • I'm not offended. I'm giving you what I consider to be good advice. My experience tells me that you will be best served by mastering simpler tasks before moving on to this one. I once sat in your chair and I remember making the same mistakes. – David Heffernan Jul 30 '15 at 12:14
  • Thank you, but honestly I do not have the luxury of time to start from scratch,especially now that Delphi is in such a decline... I do intend to do that with Java because at least I will be able to get a job if I master it, with Delphi chances are that I remain unemployed, so no big rewards for spending my time mastering Delphi. I was hoping for a quick few lines of code that I would analyze and try to comprehend the mechanism behind them. – user1137313 Jul 30 '15 at 12:33
  • In my view that's wishful thinking. But I could be wrong. It might be possible to master advanced component creation by reading a few lines of code one afternoon. Good luck. – David Heffernan Jul 30 '15 at 12:36
  • You got offended now, I am sorry. I did not make myself understood. I do not want to master the creation of ANY component and ALL that comes with it, in one afternoon, I am not stupid. I only wanted to add ONE functionality to an existing component and do not know how to do it. Yes the questions looks huge, but the idea is that simple. There are plenty of tutorials out there on component creation, but none of them talk about what I need, that is why I am here. Sorry again for annoying you – user1137313 Jul 30 '15 at 13:27
  • I already analyzed a few StringGrid derrived components, but none of them deal with what I want. I was hoping to get some fresh ideas by coming here – user1137313 Jul 30 '15 at 14:01
  • Stop using `Objects[]`, it's not intended for that. Add your own property to expose these objects. Manage their lifetime in `SizeChanged`. That's it. I'm not annoyed, I just think you are over reaching. You disagree. That's fine. – David Heffernan Jul 30 '15 at 14:16
  • `RowCount := NewColCount;` looks a little odd – David Heffernan Jul 30 '15 at 19:33
  • You are right... it is odd :)) – user1137313 Aug 01 '15 at 21:45
  • I copy/pasted that part of the code from a post by Remy Labeau (I think) and being in a hurry I did not check it's correctness or functionality... Never mind it, i'll drop the question anyway since as you argued earlier, Objects should be used by the component consumer. Thank you for your suggestions. David, please make you comment (about not using Objects at the component level) as an Answer so I would accept it, since your answer is the correct one – user1137313 Aug 01 '15 at 21:52

1 Answers1

0
// SizeChanged - Called when the size of the grid has changed.
protected
  procedure SizeChanged(OldColCount, OldRowCount: Longint); dynamic;

You can override dynamic method SizeChanged and initialize grid according to new size. You can check is it designtime or not (LU RD suggested link). And as David mentioned, it is better to keep Objects property for consumers of component. Create and use your own TList/TObjectList instead.

Community
  • 1
  • 1
Andrei Galatyn
  • 3,322
  • 2
  • 24
  • 38
  • So, it does not work, please check my edit in the question – user1137313 Jul 30 '15 at 11:19
  • What do you mean by "Create and use your own TList/TObjectList instead."? Instead of the cell's object? I do not understand how that would help me. My component is one for planning purposes (something like a Gantt grid). And I wanted to hold information inside the Cell so the consumer would have everything he needs inside the clicked cell – user1137313 Jul 30 '15 at 11:25