[Erledigt] Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
charlytango
Beiträge: 333
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

[Erledigt] Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Beitrag von charlytango »

Hi,

ich plage mich gerade etwas ab um Zellen in einem Stringgrid nach unterschiedlichen Kriterien einzufärben.
Hab da einige Beispiele gefunden und an sich klappt es ganz gut. Allerdings bin ich über unterschiedliche Codevarianten gestolpert und möchte nun wissen wie man korrekt mit solchen Events umgeht.

Als Beispiel mal ein TStringgrid.

Wird in Event auf das Objekt per Objektvariable zugegriffen oder über "Sender"

ZB

Code: Alles auswählen

procedure TfrmTracks.StringGrid1DrawCell(Sender: TObject; aCol,
  aRow: Integer; aRect: TRect; aState: TGridDrawState);
var
  ZellText:string;
begin
  If (gdFixed in aState) then exit;

  if (aCol = 3) then begin
      stringgrid1.Canvas.Brush.Color := clYellow;  //<-- auf den Stringgrid1 direkt zugreifen
      stringgrid1.Canvas.Font.Color := clBlack;
      stringgrid1.canvas.FillRect(arect);
      ZellText := stringgrid1.Cells[aCol, aRow]; // Text der Zelle holen
      StringGrid1.Canvas.TextOut(aRect.Left + 2, aRect.Top + 2, ZellText);
  end;

end;                 
oder

Code: Alles auswählen

procedure TfrmTracks.StringGrid1DrawCell(Sender: TObject; aCol,
  aRow: Integer; aRect: TRect; aState: TGridDrawState);
var
  ZellText:string;
  grid: TStringGrid;
begin
  grid := Sender as TStringGrid;  //<--- den Sender casten
  If (gdFixed in aState) then exit;

  if (aCol = 3) then begin
      grid.Canvas.Brush.Color := clYellow;    // <---und ab hier über die gecastete variable zugreifen
      grid.Canvas.Font.Color := clBlack;
      grid.canvas.FillRect(arect);
      ZellText := grid.Cells[aCol, aRow]; // Text der Zelle holen
      grid.Canvas.TextOut(aRect.Left + 2, aRect.Top + 2, ZellText);
  end;

end;                 
oder anders?
...oder ist es eh egal?

Wie ist das korrekt und sauber ?
Zuletzt geändert von charlytango am So 28. Mär 2021, 19:39, insgesamt 1-mal geändert.

Benutzeravatar
theo
Beiträge: 8636
Registriert: Mo 11. Sep 2006, 19:01

Re: Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Beitrag von theo »

Interessant wird "Sender", wenn du mehrere StringGrids hast und bei allen die gleiche Ereignisbehandlungsprozedur "einhängst".
Bei einem einzelnen Objekt ist es mMn Geschmackssache.

Sieben
Beiträge: 106
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.0.10, FPC 3.2.0
CPU-Target: i386

Re: Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Beitrag von Sieben »

Ich würde immer die zweite Variante bevorzugen, da sie, wie Theo schon sagte, generischer ist. Nicht nur wenn du mehr als ein Objekt / eine Komponente in ein und denselben Handler hängen willst, sondern zB auch, wenn du das Code-Gerüst stellenweise angepasst in einem weiteren Handler verwenden willst. Im ersten Fall müsstest du an allein hier fünf Stellen das explizite StringGrid1 durch zB StringGrid2 ersetzen, im zweiten Fall kannst du dich weit weniger fehlerträchtig auf die tatsächlichen Anpassungen konzentrieren.

Weit wichtiger finde ich aber, grundsätzlich die im Event mitgelieferten Parameter zu verwenden, wie hier zB aCol, aRow, aRect und aState - was du ja auch tust. Vermeintlich aktuelle Abfragen beim Objekt selbst können uU durchaus von den im Event übergebenen Parametern abweichen.

Benutzeravatar
h-elsner
Beiträge: 112
Registriert: Di 24. Jul 2012, 15:42
OS, Lazarus, FPC: LINUX Mint18.3, Win10, Lazarus 2.0.12, FPC3.2.0
CPU-Target: 64Bit
Wohnort: Illertissen
Kontaktdaten:

Re: Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Beitrag von h-elsner »

xyz_wp hat mir mal geraten zum Einfärben der Zellen eines StringGrids nicht den Event OnDrawCell sondern OnPrepareCanvas zu nutzen. Ich glaube das ist schneller, aber zumindest einfacher zu nutzen. Seitdem bin ich glücklich damit.

Hat zwar mit dem eigentlichen problem nichts zu tun, fiel mir aber in dem Zusammenhang wieder ein. Betreffs "Sender" oder nicht sehe ich es generell so wie Theo.

Gruß HE

charlytango
Beiträge: 333
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Beitrag von charlytango »

h-elsner hat geschrieben:
So 28. Mär 2021, 09:42
xyz_wp hat mir mal geraten zum Einfärben der Zellen eines StringGrids nicht den Event OnDrawCell sondern OnPrepareCanvas zu nutzen. Ich glaube das ist schneller, aber zumindest einfacher zu nutzen. Seitdem bin ich glücklich damit.
Diesen Post habe ich auch gefunden und so ausprobiert, leider ohne Erfolg.
Nach vielen Google-Recherchen und Tests hab ich mit Mühe und Not ;) die Variante in OnDrawCell hin bekommen.
Besonders fies war bei fast allen gefundenen Beispielen dass zwar immer gezeigt wurde wie man die Zelle einfärbt

Code: Alles auswählen

stringgrid1.canvas.FillRect(arect);
aber damit war dann auch der Zellinhalt nicht mehr zu lesen.

Dass man den Text erneut zeichnen muss hab ich erst viel später kapiert.

Code: Alles auswählen

      ZellText := stringgrid1.Cells[aCol, aRow]; // Text der Zelle holen
      StringGrid1.Canvas.TextOut(aRect.Left + 2, aRect.Top + 2, ZellText);
Nun gibt es im OnPrepareCanvas Event aber keinen aRect Parameter um mit

Code: Alles auswählen

stringgrid1.canvas.FillRect(arect);
die Zelle einzufärben.

Bin aber für Verbesserungen zugänglich :)

TraumTaenzerDieter
Beiträge: 26
Registriert: So 14. Aug 2011, 09:11

Re: Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Beitrag von TraumTaenzerDieter »

Im OnPrepareCanvas brauchst Du die Zelle nicht zu füllen!
Genau dafür ist dieses Event zuständig: siehe Testprojekt im Anhang.
Dateianhänge
Test.zip
(2.04 KiB) 14-mal heruntergeladen

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

Re: Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Beitrag von wp_xyz »

Nur um Zellen einzufärben, brauchst du kein OnDrawCell. OnPrepareCanvas ist völlig ausreichend. Bevor der Zellinhalt gezeichnet wird wird PrepareCanvas aufgerufen, wo die Canvas-Eigenschaften (Brush, Font, etc) gesetzt werden; am Ende dieser Routine wird das Ereignis OnPrepareCanvas ausgelöst, in dem du die gewählten Einstellungen nochmals ändern kannst. Damit wird dann endgültig die Ausgabe gemalt.

Wenn du im Internet immer Hinweise auf OnDrawCell findest, so liegt das an Delphi, wo das Grid kein OnPrepareCanvas-Event hat.

charlytango
Beiträge: 333
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Wie ist guter Code in Events (aktuell TStringgrid.DrawCell)

Beitrag von charlytango »

wp_xyz hat geschrieben:
So 28. Mär 2021, 16:21
Nur um Zellen einzufärben, brauchst du kein OnDrawCell. OnPrepareCanvas ist völlig ausreichend. Bevor der Zellinhalt gezeichnet wird wird PrepareCanvas aufgerufen, wo die Canvas-Eigenschaften (Brush, Font, etc) gesetzt werden; am Ende dieser Routine wird das Ereignis OnPrepareCanvas ausgelöst, in dem du die gewählten Einstellungen nochmals ändern kannst. Damit wird dann endgültig die Ausgabe gemalt.

Danke für das Testprogramm -- hab es ausprobiert und "works like a charm".
Hatte nur das "Problem" dass mit einer Auswahlzeile der Text völlig verschwand, was eigentlich nur eine optische Täuschung war, denn er wurde genauso wie auf dem anderen Auswahlbalken weiß dargestellt. (nur weiß auf gelb ist keine glückliche Kombination)
Deswegen steuere ich noch einen Codeteil bei mit dem die ausgewählte Zelle angepasst werden kann.

Code: Alles auswählen

procedure TfrmBrowse.StringGrid1PrepareCanvas(sender: TObject; aCol,
  aRow: Integer; aState: TGridDrawState);
var
  grid: TStringGrid;
begin
  If (gdFixed in aState) then exit;

  grid := Sender as TStringGrid;

  if (aCol = 3) then begin
      if (gdSelected in aState) or (gdFocused in aState) then //Wenn die Zelle ausgewählt ist
      begin
        grid.Canvas.Font.Color := clblack;
        grid.Canvas.Brush.Color := clYellow;
      end
      else
        grid.Canvas.Brush.Color := clYellow;
  end;

end; 


Danke f die Hilfe, case closed

Antworten