[erledigt] TDrawGrid mit Checkbox

Rund um die LCL und andere Komponenten
Antworten
MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

[erledigt] TDrawGrid mit Checkbox

Beitrag von MacWomble »

Ich versuche eine Checkbox in ein (dynamisch erzeugtes) TDrawgrid einzufügen. Ich bekomme aber den Buttonstyle nicht gesetzt (letzte Zeile).
Die Meldung ist 'Index out of bounds', weshalb ich vermute, dass die Column an dieser Stelle noch nicht existiert.
Wo führe ich das am besten aus bzw. kann mir jemand sagen, was ich hier falsch mache?

Code: Alles auswählen

  { Einstellungen DrawGrid }
  dgAdresse.Colcount := dgAdresse.FixedCols + 9; // Wert an GetCellText anpassen!
 
  dgAdresse.RowCount := Adressliste.Count + dgAdresse.FixedRows;
  dgAdresse.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRowSelect, goSmoothScroll, goRowHighlight, goEditing];
  dgAdresse.ScrollBars := ssVertical;
  dgAdresse.FixedCols := 1;
  dgAdresse.FixedRows := 1;
 
   v := (dgAdresse.Width - 15 - 920) div 6; { 1 variable Spalte v }
  dgAdresse.ColWidths[0] := 30;   // Width 715
  dgAdresse.ColWidths[1] := 20// Checkbox
  ...
  dgAdresse.ColWidths[9] := 100 + v;  // Straße
 
  dgAdresse.columns[1].ButtonStyle:=cbsCheckBoxColumn;  // Checkbox   
Zuletzt geändert von MacWomble am So 29. Sep 2019, 08:44, insgesamt 3-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

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

Re: TDrawGrid mit Checkbox

Beitrag von wp_xyz »

Ich sehe da ColCount/ColWidths[] und Columns[] im selben Code-Abschnitt. Das geht nicht. Entweder ColCount/ColWidths oder Columns. Ersteres ist das Verfahren von Delphi, letzteres vereinfacht den Umgang mit Spalten, ähnlich wie im DBGrid. Da du offenbar mit dem ButtonStyle der Columns arbeiten willst, musst du das Columns-Konzept verwenden - das heißt: im Objekt-Inspektor oder per Code soviele Spalten erzeugen wie du brauchst und dann kannst du die entsprechenden Properties setzen.

Ungetestet:

Code: Alles auswählen

// All die im folgenden gemachten Änderungen kann man auch stattdessen im Objekt-Inspektor vornehmen
procedure TForm1.Form1Create(Sender: TObject);
var
  i: Integer;
begin
  ...
  // 9 Spalten erzeugen - im Column-Konzept kann man nicht ColCount setzen!
  for i := 1 to 9 do
    dgAdresse.Columns.Add;
  // Breite der FixedCol setzen (anders kommt man hier nicht an die FixedCol ran)
  dgAdresse.DefaultColWidth := 30;   
  // 1. Daten-Spalte
  dgAdressen.Columns[0].Width := 20
  dgAdressen.Columns[0].ButtonStyle := cbsCheckboxColumn;  // Checkbox
  dgAdressen.Columns[0].Title := 'bla-bla';
  dgAdressen.Columns[0].SizePriority := 0;
  // 2. Daten-Spalte
  dgAdressen.Columns[1].Title := 'bla-bla';
  dgAdressen.Columns[1].Width := ...
  dgAdressen.Columns[1].SizePriority := 0;
  ...
  // 9. Daten-Spalte
  dgAdressen.Columns[8].Title := 'bla-bla';
  dgAdressen.Columns[8].SizePriority := 1; // kann man auch weglassen, da der Default-Wert 1 ist.
  // Automatische Spaltenbreitenanpassung aktivieren
  dgAdressen.AutoFillColumns := true// hält die Breite der Spalten mit SizePriority=0 konstant, passt die Breite der Spalte mit SizePriority 1 an den verfügbaren Platz an.

Hinweis: Das Columns-Konzept der LCL-Grids hat den Nachteil der verwirrenden Spalten-Indizierung, wenn eine FixedCol vorhanden ist: Die 1.DatenSpalte, Columns[0], hat dann den Index 0, in der klassischen Indizierung aber den Index 1! Da sind schon einige drauf reingefallen.

Siehe auch: https://wiki.lazarus.freepascal.org/Gri ... ty_Columns das ich gerade um einen Abschnitt über AutoFillColumns ergänzt habe.
Zuletzt geändert von wp_xyz am Sa 28. Sep 2019, 12:55, insgesamt 2-mal geändert.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: TDrawGrid mit Checkbox

Beitrag von MacWomble »

Herzlichen Dank für die aufschlussreichen Erklärungen, da hätte ich mir wahrscheinlich sonst das ganze Wochenende versaut damit ;-)

In diesem Sinne ein schönes Wochenende!
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: TDrawGrid mit Checkbox

Beitrag von MacWomble »

Es gibt doch noch ein weiteres Problem.

Ich habe nun:

Code: Alles auswählen

procedure TfrAdressliste.InitGridAdresse;
var
  v: integer;
  i: integer;
begin
  { Einstellungen DrawGrid }
  dgAdresse.FixedCols := 1;
  dgAdresse.FixedRows := 1;
  dgAdresse.Colcount := dgAdresse.FixedCols + 8; // Wert an GetCellText anpassen!
  dgAdresse.RowCount := Adressliste.Count + dgAdresse.FixedRows;
  dgAdresse.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRowSelect, goSmoothScroll, goRowHighlight, goEditing];
  dgAdresse.ScrollBars := ssVertical;
 
  // 9 Spalten erzeugen - im Column-Konzept kann man nicht ColCount setzen!
  for i := 1 to 9 do
    dgAdresse.Columns.Add;
  // Breite der FixedCol setzen (anders kommt man hier nicht an die FixedCol ran)
  dgAdresse.DefaultColWidth := 30;
 
  v := (dgAdresse.Width - 15 - 920) div 7; { 1 variable Spalte v }
  //dgAdresse.columns[0].Width := 30;   // Width 715
  dgAdresse.columns[0].Width := 25// Checkbox
  dgAdresse.Columns[0].ButtonStyle:=cbsCheckboxColumn;
  //dgAdresse.Columns[0].va;
  dgAdresse.columns[1].Width := 100// Anrede
  dgAdresse.columns[2].Width := 150 + v;  // Name
  dgAdresse.columns[3].Width := 100 + v;  // Vorname
  dgAdresse.columns[4].Width := 150 + v;  // Firma
  dgAdresse.columns[5].Width := 150 + v;  // Zusatz
  dgAdresse.columns[6].Width := 60// PLZ
  dgAdresse.columns[7].Width := 80 + v;  // Ort
  dgAdresse.columns[8].Width := 100 + v;  // Straße
end;                   


Das funktioniert auch so weit. Ferner habe ich die folgenden Events sowie ein Property 'Checked' angelegt:

Code: Alles auswählen

procedure TfrAdressliste.dgAdresseCheckboxToggled(sender: TObject; aCol,
  aRow: Integer; aState: TCheckboxState);
var
  lData: TAdresse;
 
begin
  if (ARow > 0) and (ACol = 0) then
  begin
    lData := AdressListe[ARow - 1];
    lData.Checked := not lData.Checked;
  end;
end
 
procedure TfrAdressliste.dgAdresseGetCheckboxState(Sender: TObject; ACol,
  ARow: Integer; var Value: TCheckboxState);
var
  lData: TAdresse;
 
begin
  if ACol = 0 then
  begin
    lData := AdressListe[ARow - 1];
    if lData.Checked then
      Value := cbChecked
    else
      Value := cbUnchecked;
  end;
end


Ich sehe aber keine Checkbox und auch beim Klicken passiert nichts.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

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

Re: TDrawGrid mit Checkbox

Beitrag von wp_xyz »

Nachdem TDrawGrid nichts enthält was die angezeigten Daten speichert (anders als TStringGrid), benötigt man irgend eine Datenstruktur für die anzuzeigenden Daten. Du hast dafür die Klasse TAdresse - richtig. Das Feld TAdresse.Checked ist offenbar für den Zustand der Checkbox im Grid zuständig - richtig. Um zu bestimmen, ob die Checkbox im Grid als "checked" oder "unchecked" dargestellt wird, hat man das Ereignis OnGetCheckboxState: Man liest aus der Datenstruktur aus, ob der Zustand als "checked" oder "unchecked" gilt und überträgt das Ergebnis in die dem Event als Parameter übergebene Variable Value - so wie du es machst, ist es richtig.

Aber: Wenn man mit der Maus auf der Checkbox klickt, muss der neue Zustand wieder in die Datenstruktur zurück geschrieben werden. Hierfür ist das Ereignis OnSetCheckBoxState zuständig - das übliche Set/Get-Pärchen:

Code: Alles auswählen

procedure TfrAdressliste.dgAdressegSetCheckboxState(Sender: TObject; ACol, ARow: Integer; const Value: TCheckboxState);
var
  lData: TAdresse:
begin
  if ACol > 0 then
    exit;
  lData := AdressListe[ARow - dgAdresse.FixedRows];
  lData.Checked := (Value = dChecked);
end;

Das von dir verwendete Ereignis OnCheckBoxToggled hat für den Datentransfer keine Bedeutung und ist eher als Signal-Geber zu verstehen, über den andere Komponenten mitkriegen können, dass sich der Check-State geändert hat.

Einfaches Beispiel anbei.
Dateianhänge
DrawGrid_Checkbox.zip
(2.22 KiB) 129-mal heruntergeladen

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: TDrawGrid mit Checkbox

Beitrag von MacWomble »

Leider habe ich nach wie vor das Problem:

Von der Logik her funktioniert es wie es soll aber die Checkboxen werden nicht angezeigt.

Dein Beispielprojekt läuft aber und dort sehe ich die Checkboxen auch.

Anscheinend gibt es im Drawgrid weitere Sachen, welche zu unerwarteten Ergebnissen führen könne.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

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

Re: TDrawGrid mit Checkbox

Beitrag von wp_xyz »

Vielleicht hast du DefaultDrawing auf False gesetzt?

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: TDrawGrid mit Checkbox

Beitrag von MacWomble »

Nein, das ist true...

Ich habe nun gefunden, woran es liegt:

Code: Alles auswählen

procedure TfrAdressliste.dgAdresseDrawCell(Sender: TObject; aCol, aRow: integer; aRect: TRect; aState: TGridDrawState);
var
  txt: string;
  TextStyle: TTextStyle;
begin
  { Zellinhalt Holen }
  txt := GetCellText(ACol, ARow);
  { Spalte Fett }
  if Acol = 2 then
    dgAdresse.Canvas.Font.Style := [fsBold];
 
  { Farben setzen }
  if A.IDColor > 0 then
 
    if (aCol > 0) and (aRow >= dgAdresse.FixedRows) then
    begin
      if AState * [gdFocused, gdSelected] <> [] then
      begin
        dgAdresse.Canvas.Font.Color := StringToColor(A.Colorhfont);
        dgAdresse.Canvas.Brush.Color := StringToColor(A.Colorh);
      end
      else
      begin
        dgAdresse.Canvas.Font.Color := StringToColor(A.Colornfont);
        dgAdresse.Canvas.Brush.Color := StringToColor(A.Colorn);
      end;
    end;
 
  { Textstyle definieren }
  if aRow = 0 then
    TextStyle.Alignment := taCenter
  else
    case ACol of
      0: TextStyle.Alignment := taCenter;
      1: TextStyle.Alignment := taCenter; //Anrede
      2: TextStyle.Alignment := taLeftJustify; // Name
      3: TextStyle.Alignment := taLeftJustify; // Vorname
      4: TextStyle.Alignment := taLeftJustify; // Firma
      5: TextStyle.Alignment := taLeftJustify;      // Zusatz
      6: TextStyle.Alignment := taCenter;      // PLZ
      7: TextStyle.Alignment := taLeftJustify;// Ort
      8: TextStyle.Alignment := taLeftJustify;// Straße
    end;
  TextStyle.Layout := tlCenter;
  TextStyle.SingleLine := True;
  TextStyle.Clipping := True;
  TextStyle.ExpandTabs := True;
  TextStyle.ShowPrefix := True;
  TextStyle.Wordbreak := True;
  TextStyle.Opaque := False;
  TextStyle.SystemFont := False;
  TextStyle.RightToLeft := False;
  TextStyle.EndEllipsis := False;
 
  dgAdresse.Canvas.FillRect(ARect);
  InflateRect(ARect, -varCellpadding, -varCellpadding);   // Rechteck kleiner machen
  dgAdresse.Canvas.TextRect(ARect, ARect.Left + constCellPadding, ARect.Top + constCellPadding, txt, TextStyle);
end


Wenn ich die drtittletzte Zeile

Code: Alles auswählen

dgAdresse.Canvas.FillRect(ARect);
durch

Code: Alles auswählen

if (aCol > 1) then dgAdresse.Canvas.FillRect(ARect);

ersetze, dann funktioniert es zwar, aber die Spalte ist nicht mehr farbig und er schreibt mir - zusätzlich - 'Title' in den Titel der Spalte.

Da es ja jetzt erst mal so funktioniert und der Fehler lokalisiert ist, setze ich den Thread jetzt auf erledigt.
Anscheinend ist TDrawGrid sehr vielseitig und leistungsfähig, stellenweise aber auch extrem verwirrend.

Ich versuche noch weiter, das etwas schöner zu machen. Ich könnte mir auch gut vorstellen, die Checkbox in einer zweiten FixedCol zu haben.

Jedenfalls haben mir deine Hinweise sehr weiter geholfen - nochmals meinen Dank dafür! :D
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

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

Re: [erledigt] TDrawGrid mit Checkbox

Beitrag von wp_xyz »

Bei den Lazarus Grids gibt es das OnPrepareCanvas-Event, das aufgerufen wird, bevor der Zellinhalt geschrieben wird. Hier hat man die Gelegenheit, die bereits vorgegebenen Default-Eigenschaften des Grid-Canvas zu ändern. Das FillRect für den Zellhintergrund wird dann mit dem entsprechend festgelegten Brush erledigt, ohne dass man das in OnDrawCell nochmals machen muss und dabei evtl etwas überschreibt.

Code: Alles auswählen

    procedure TfrAdressliste.dgAdressePrepareCanvas(Sender: TObject; aCol, aRow: integer; aRect: TRect; aState: TGridDrawState);
    var
      TextStyle: TTextStyle;
    begin
      { Spalte Fett }
      if Acol = 2 then
        dgAdresse.Canvas.Font.Style := [fsBold];
 
      { Farben setzen }
      if A.IDColor > 0 then
 
        if (aCol > 0) and (aRow >= dgAdresse.FixedRows) then
        begin
          if AState * [gdFocused, gdSelected] <> [] then
          begin
            dgAdresse.Canvas.Font.Color := StringToColor(A.Colorhfont);
            dgAdresse.Canvas.Brush.Color := StringToColor(A.Colorh);
          end
          else
          begin
            dgAdresse.Canvas.Font.Color := StringToColor(A.Colornfont);
            dgAdresse.Canvas.Brush.Color := StringToColor(A.Colorn);
          end;
        end;
 
      { Textstyle definieren }
      TextStyle := dgAdresse.Canvas.TextStyle;   // <--- Richtig initialisieren
      if aRow = 0 then
        TextStyle.Alignment := taCenter
      else
        case ACol of
          0: TextStyle.Alignment := taCenter;
          1: TextStyle.Alignment := taCenter; //Anrede
          2: TextStyle.Alignment := taLeftJustify; // Name
          3: TextStyle.Alignment := taLeftJustify; // Vorname
          4: TextStyle.Alignment := taLeftJustify; // Firma
          5: TextStyle.Alignment := taLeftJustify;      // Zusatz
          6: TextStyle.Alignment := taCenter;      // PLZ
          7: TextStyle.Alignment := taLeftJustify;// Ort
          8: TextStyle.Alignment := taLeftJustify;// Straße
        end;
      TextStyle.Layout := tlCenter;    // hier könnte man einiges weglassen, da bei Initialisierung erledigt...
      TextStyle.SingleLine := True;
      TextStyle.Clipping := True;
      TextStyle.ExpandTabs := True;
      TextStyle.ShowPrefix := True;
      TextStyle.Wordbreak := True;
      TextStyle.Opaque := False;
      TextStyle.SystemFont := False;
      TextStyle.RightToLeft := False;
      TextStyle.EndEllipsis := False;
 
      dgAdresse.Canvas.TextStyle := TextStyle;
    end;
 
    procedure TfrAdressliste.dgAdresseDrawCell(Sender: TObject; aCol, aRow: integer; aRect: TRect; aState: TGridDrawState);
    var
      txt: string;
    begin
      txt := GetCellText(ACol, ARow);
      InflateRect(ARect, -varCellpadding, -varCellpadding);   // Rechteck kleiner machen
      dgAdresse.Canvas.TextRect(ARect, ARect.Left, ARect.Top, txt);
    end;   

Zum zweiten Punkt: Wenn "Title" in der Titelzeile steht, hast du den Column.Title.Caption nicht gesetzt.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [erledigt] TDrawGrid mit Checkbox

Beitrag von MacWomble »

Ich staune und frage mich so langsam, woher du deine Informationen beziehst. Ich suche mich im Netz dumm und dämlich zu dem Thema, im fpc-Wiki ist TDrawgrid nicht einmal erklärt.
Dabei scheint das Teil ja sehr mächtig und vielseitig zu sein...

Was übersehe ich? :oops:

// hier könnte man einiges weglassen, da bei Initialisierung erledigt...

Da ich das Drawgrid zuerst eingebaut habe und später erst auf dynamisch umstellte (nachdem ich versch. Sachen probiert hatte) habe ich einfach - sicherheitshalber - alle relevanten Eigenschaften hier gesetzt ohne zu prüfen.

Zum zweiten Punkt: Wenn "Title" in der Titelzeile steht, hast du den Column.Title.Caption nicht gesetzt.

Das habe ich inzwischen geändert. Ich habe die Titel ja zuvor auf einem anderen Weg an ihre Stelle gebracht. Es waren sowohl die title.caption als auf der von mir übergebene Titeltext übereinander angezeigt.

Ich muss ohnehin aufgrund der Umstellung auf columns noch einmal etwas nacharbeiten.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

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

Re: [erledigt] TDrawGrid mit Checkbox

Beitrag von wp_xyz »

MacWomble hat geschrieben:Ich staune und frage mich so langsam, woher du deine Informationen beziehst. Ich suche mich im Netz dumm und dämlich zu dem Thema, im fpc-Wiki ist TDrawgrid nicht einmal erklärt.
Dabei scheint das Teil ja sehr mächtig und vielseitig zu sein...

https://wiki.lazarus.freepascal.org/Gri ... rence_Page - bezieht sich zwar eher auf TStringGrid, aber da TStringGrid und TDrawGrid im Prinzip alles von TCustomGrid erben, steht auf dieser Seite das meiste. Die Seite gehört für jeden Grid-User in die Bookmarks!

Die wiki-Seite für DrawGrid (https://wiki.lazarus.freepascal.org/TDrawGrid) ist zwar etwas dünn, aber enthält die richtigen Querverweise.
Zuletzt geändert von wp_xyz am So 29. Sep 2019, 13:26, insgesamt 1-mal geändert.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [erledigt] TDrawGrid mit Checkbox

Beitrag von MacWomble »

Die Grid-Seite habe ich tatsächlich ignoriert :shock: :oops: - Danke :D
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [erledigt] TDrawGrid mit Checkbox

Beitrag von MacWomble »

Jetzt habe ich das bei einer anderen Liste versucht, aber dort wird SetCheckboxState nicht aufgerufen, wenn ich die Box anklicke :-(

Lösung: In Options war goEditing deaktiviert! :oops:
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Antworten