ExtStringGrid und OnDrawCell

Rund um die LCL und andere Komponenten
Antworten
lzuser
Beiträge: 97
Registriert: Sa 20. Jun 2009, 16:00
OS, Lazarus, FPC: Win10 20H2, Laz 2.0.8 auch Linux Mint Mate 20, Laz 2.0.6
CPU-Target: 64Bit

ExtStringGrid und OnDrawCell

Beitrag von lzuser »

Laz 1.8.4 Win10
In einer neuen Unit habe ich mir eine erweiterte Komponente definiert.
Ich möchte im Programm nur noch durch einige Prozeduren z.B. Farben zuweisen.
Die neue Komponente soll diese dann automatisch in DrawCell wie unten auswerten.

Code: Alles auswählen

 
TExtStringGrid = class(TStringGrid)
  private
  protected
  public
    CellProps:tCellPropArray;
    procedure OnDrawCell ???  //override; ?
    procedure ZeileFaerben...//später dazu
  published
  end;
 
procedure TExtStringGrid.OnDrawCell(Sender: TObject; aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState);
begin
//hier sollen die Eigenschaften aus den CellProps (Farbe, Schrift...)
//ausgewertet werden, z.B. irgendwie so:
With Sender as TExtStringGrid do
  begin
  Canvas.Brush.Color:=CellProps[aCol,aRow].Color;
  Canvas.FillRect(aRect);
  end;
end;
 

Ich bringe es nicht zueinander, dass die erweiterte Komponente automatisch diese Prozedur benutzt.
Über ein wenig (Nach-)Hilfe würde ich mich freuen. Danke.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: ExtStringGrid und OnDrawCell

Beitrag von wp_xyz »

Wichtigste Frage: Brauchst du das öfter? Wenn nicht würde ich das mit einem Standard-Grid machen: einfach einen Handler für OnPrepareCanvas schreiben und dort die Farben für die einzelnen Zellen entsprechend setzen. Denn OnPrepareCanvas wird aufgerufen, nachdem das Grid bereits die vordefinierten Farben, Fonts usw. gesetzt hat und bevor die Ausgabe erfolgt; du hast also die Möglichkeiten die Voreinstellungen mit eigenen Wünschen zu überschreiben.

Wenn du trotzdem eine eigene Komponente schreiben willst, dann ist der Ansatz mit dem OnDrawCell falsch. Denn der Anwender deiner Komponente hat nun keine Möglichkeit mehr, seinerseits das onDrawCell-Event zu verwenden. Als Komponentenschreiber hast du aber die Möglichkeit, auf "protected" Methoden zurückzugreifen; darunter findest du in der Regel immer eine Methode, die du überschreiben kannst ohne das Event zu benötigen. Wichtig: die methode muss "virtuell" sein, also den Zusatz "virtual", "override" oder "dynamic" tragen, damit sie zur Laufzeit statt der geerbten aufgerufen wird.

Studiere den Quellcode der Ursprungskomponente, um den richtigen Einsprungpunkt zu finden. Ohne die Komponente verstanden zu haben, wird deine abgeleitete Komponente wahrscheinlich nicht funktionieren.

Wenn du wirklich nur Farben, Fonts usw. verändern möchtest, dann würde ich genau wie oben wieder auf den PrepareCanvas-Zug "aufspringen". Die passende Methode wäre hier DoPrepareCanvas, bei dem alle Standardarbeiten schon erledigt sind und nur noch das Ereignis OnPrepareCanvas aufgerufen wird. Hier kannst du zuerst deine Änderungen vornehmen, und dann "inherited" aufrufen, so dass das Ereignis dem Anwender weiterhin zur Verfügung steht. Etwa so:

Code: Alles auswählen

procedure TExtStringGrid.DoPrepareCanvas(aCol,aRow:Integer; aState: TGridDrawState); 
var
  cp: TCellProp;
begin
  cp := CellPropArray[aCol, aRow];
  Canvas.Brush.Color := cp.Color;
  Canvas.Font.Color := cp.FontColor;   // oder was auch immer...
  inherited;
end;

Wiegesagt, diese Methode macht selbst keine Ausgabe, sondern überlässt dies der dem Grid-Vorfahren, der dies gleich anschließend erledigt. Das hat für dich den Vorteil, dass du dich nicht mehr darum kümmern musst, dass der Text richtig positioniert wird, evtl. Checkboxen oder Icons in der Zelle gezeichnet werden uvm.

lzuser
Beiträge: 97
Registriert: Sa 20. Jun 2009, 16:00
OS, Lazarus, FPC: Win10 20H2, Laz 2.0.8 auch Linux Mint Mate 20, Laz 2.0.6
CPU-Target: 64Bit

Re: ExtStringGrid und OnDrawCell

Beitrag von lzuser »

Danke für die Erläuterungen. Das funktioniert gut soweit ich das bisher (nur mit Farben) probiert habe. DoPrepareCanvas funktioniert mit override, da bekam ich bei DrawCell immer einen Fehler.

Mit ist aufgefallen, dass für jede Zelle ein Brush erst erzeugt werden muss, aber das bekomme ich wohl auch hin.

Aber weiterhin fiel mir auf, dass ich meine CellProps idealerweise als dynamisches array mit ColCount und RowCount realisieren sollte, weil die ja auch zur Laufzeit verändert werden können. Bevor ich lange suche: Gibt es dafür fertige/übliche Prozeduren in Lazarus mit einer Anleitung/Beispiel?
Danke für eine weitere Hilfe.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: ExtStringGrid und OnDrawCell

Beitrag von wp_xyz »

lzuser hat geschrieben:Mit ist aufgefallen, dass für jede Zelle ein Brush erst erzeugt werden muss, aber das bekomme ich wohl auch hin.

Erzeugen? Create? Nein, der Brush wird vom Canvas verwaltet (erzeugt und zerstört), da darfst du nicht dazwischenfunken. Du musst nur die Properties entsprechend setzen.

lzuser hat geschrieben:Aber weiterhin fiel mir auf, dass ich meine CellProps idealerweise als dynamisches array mit ColCount und RowCount realisieren sollte, weil die ja auch zur Laufzeit verändert werden können. Bevor ich lange suche: Gibt es dafür fertige/übliche Prozeduren in Lazarus mit einer Anleitung/Beispiel?

"dafür" - wofür? Für die Dimensionierung eines dynamischen Arrays? Einfach SetLength:

Code: Alles auswählen

var
  CellProps: array of array of TCellProp;
...
  SetLength(CellProps, ColCount, RowCount);

Vielleicht sollte ich aber noch erwähnen, dass das Grid intern schon für sowas vorbereitet ist. Intern gibt es ein mit "FGrid" bezeichnetes "TVirtualGrid" mit Properties "Celda[Col, Row: integer]: PCellProps" für jede besetzte Zelle - leider ist hier vieles in Portugiesich (?). PCellProps ist ein Zeiger auf einen TCellProps-Record:

Code: Alles auswählen

type
  TCellProps=record
    Attr: pointer;
    Data: TObject;
    Text: pchar;
  end;

Du könntest jetzt für Attr einen weiteren Record mit den gewünschten Zell-Farben, Fonts etc. einführen; damit müsstest du dich um die Speicherung/Suche nach Attributen nicht mehr kümmern, das Basisgrid, TCustomGrid, kann das schon. Gerade zum Thema Speicherung wäre zu sagen, dass ein 2D-Array sehr verschwenderisch mit Speicher umgeht, weil jede leere Zelle einen Eintrag erhält. Das TVirtualGrid dagegen speichert nur etwas für die besetzten Zellen. Allerdings musst du etwas tiefer ins Innenleben des Grids eintauchen. Schau dir die Getter/Setter-Methoden GetCells und SetCells für die Standard-Eigenschaft Cells[col,row] des Stringgrid an - da siehst du wie auf Celda zugegriffen wird.

lzuser
Beiträge: 97
Registriert: Sa 20. Jun 2009, 16:00
OS, Lazarus, FPC: Win10 20H2, Laz 2.0.8 auch Linux Mint Mate 20, Laz 2.0.6
CPU-Target: 64Bit

Re: ExtStringGrid und OnDrawCell

Beitrag von lzuser »

wp_xyz hat geschrieben:Erzeugen? Create? Nein, der Brush wird vom Canvas verwaltet (erzeugt und zerstört), da darfst du nicht dazwischenfunken. Du musst nur die Properties entsprechend setzen.

Vielleicht habe ich mich falsch ausgedrückt:
Wenn ich die Brush für jede Zelle einzeln selbst verwalten will (z.B. TCellProp=Record MyBrush:TBrush; ... end;) dann muss ich doch jeden MyBrush mit Create erzeugen, oder verwaltet ein StringGrid das irgendwo so intern?

Ansonsten vielen Dank für die Erläuterungen, viel "Forschungsarbeit", als sehen wie "tief" sich lohnt.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: ExtStringGrid und OnDrawCell

Beitrag von wp_xyz »

lzuser hat geschrieben:
wp_xyz hat geschrieben:Wenn ich die Brush für jede Zelle einzeln selbst verwalten will (z.B. TCellProp=Record MyBrush:TBrush; ... end;) dann muss ich doch jeden MyBrush mit Create erzeugen, oder verwaltet ein StringGrid das irgendwo so intern?

Ich denke schon, dass du die Brushes selbst erzeugen und entsorgen musst. Allerdings: Ist das nicht ein Handle des Betriebssystems? Die sind begrenzt. Daher würde ich eher dazu tendieren, nicht TBrush-Objekte zu speichern, sondern nur die Properties, und damit könnte man auch das Problem umgehen, dass bei Brush.Style <> bsSolid die Hintergrundfarbe nicht spezifiziet werden kann:

Code: Alles auswählen

type
  TCellProp = record
    FillStyle: TBrushStyle;
    FillColor: TColor;
    BkColor: TColor;  // bei zweifarbigen Fills (FillStyle <> bsSolid): Zelle zuerst mit bsSolid und BkColor füllen, dann mit FillStyle und FillColor
    //...
  end;

Antworten