DBGrid Multiline ?

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Antworten
DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

DBGrid Multiline ?

Beitrag von DL3AD »

Hallo,

gibt es eine Möglichkeit das DBGrid als Multiline zu konfigurieren ?
D.h. Eine Datenbankzeile in mehrere Zeilen eines DBGrid unterzubringen um horzontalen Platz zu sparen ?

Gruß Frank

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

Re: DBGrid Multiline ?

Beitrag von wp_xyz »

Das geht ganz einfach, wenn man im Ereignis OnPrepareCanvas den Canvas auf mehrzeilige Textausgabe umstellt:

Code: Alles auswählen

procedure TForm1.DBGrid1PrepareCanvas(sender: TObject; DataCol: Integer;
  Column: TColumn; AState: TGridDrawState);
var
  ts: TTextStyle;
begin
  ts := DBGrid1.Canvas.TextStyle;
  ts.SingleLine := SpinEdit1.Value = 1;    // Mit dem SpinEdit kann man die Anzahl der Textzeilen einstellen.
  ts.Wordbreak := SpinEdit1.Value > 1;
  DBGrid1.Canvas.TextStyle := ts;
end;

Man muss noch darauf achten, dass die Zeilenhöhe angepasst wird. In meinem Beispiel stellt man die Zeilenhöhe mit einem SpinEdit ein, die DefaultRowheight wird aus der Höhe einer Zeile und der Anzahl der Zeilen berechnet (plus den im Grid standardmäßigen Rand von 2x constCellPadding). Leider wird dabei auch die Höhe der Titelzeile verändert, was in der Regel unschön ist. Um das zu verhindern, speichere ich im FormCreate-Ereignis die Anfangs-DefaultrowHeight ab, und setze später nach dem Ändern des SpinEdit die Höhe der Titelzeile auf diesen Wert zurück. Da beim DBGrid die Höhen der Titelzeilen nicht public sind (warum eigentlich?), muss man sich durch einen Typecast ins Grid hacken.

Code: Alles auswählen

type
  TMyDBGrid = class(TDBGrid);
  // Wenn man eine Typumwandlung des Grid auf TMyDBGrid durchführt, hat man Zugriff auf die protected Eigenschaften
 
var
  h: Integer;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  h := DBGrid1.DefaultRowHeight
end;
 
procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
  DBGrid1.DefaultRowHeight := DBGrid1.Canvas.TextHeight('A') * SpinEdit1.Value + 2*constCellPadding;;
  TMyDBGrid(DBGrid1).RowHeights[0] := h;
end;

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: DBGrid Multiline ?

Beitrag von DL3AD »

Hallo wp_xyz,

ich habe das mal realisiert - funktioniert gut - Danke!
Dazu habe ich noch weitergehende Fragen.
In der nun höheren Zeile ist der Text mittig in der Höhe zentriert - gibt es eine Einstellmöglichkeit dass der Text nach oben orientiert ist, anstatt mittig ?
Es erfolgt ja erst ein Zeilenumbruch wenn der Zelleninhalt zu lang ist für eine Zeile - ok ist eine Feststellung.

Nun habe ich folgendes vor:
Ich habe eine Spalte in der wird das Datenbankfeld "Country" dargestellt.
Nun ist die Zeilenhöhe vergrößert worden so dass nun zwei Zeilen untereinander hineinpassen.
In der oberen Zeile von "Country" soll auch Country ausgegeben werden.
In der Zeile darunter also quasi nach dem Zeilenumbruch möchte ich ein weiteres Datenbankfeld "Info" ineinfrickeln - ich hoffe du hast verstanden was ich meine.

Also irgendwie müsste eine Zeilenumbruch erzeugt werden und nsch dem Zeilenumbruch das zusätzliche Datenbankfeld "Info" angehängt werden.

Gruß Frank

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

Re: DBGrid Multiline ?

Beitrag von wp_xyz »

DL3AD hat geschrieben:In der nun höheren Zeile ist der Text mittig in der Höhe zentriert - gibt es eine Einstellmöglichkeit dass der Text nach oben orientiert ist, anstatt mittig ?

Genauso. Nimm wieder das OnPrepareCanvas-Ereignis und setze zusätzlich auch das Element Layout von TextStyle auf tlTop

Code: Alles auswählen

procedure TForm1.DBGrid1PrepareCanvas(sender: TObject; DataCol: Integer;
  Column: TColumn; AState: TGridDrawState);
var
  ts: TTextStyle;
begin
  ts := DBGrid1.Canvas.TextStyle;
  ts.SingleLine := SpinEdit1.Value = 1;    // Mit dem SpinEdit kann man die Anzahl der Textzeilen einstellen.
  ts.Wordbreak := SpinEdit1.Value > 1;
  ts.TextStyle := tlTop;              // Text vertikal oben ausrichten
  DBGrid1.Canvas.TextStyle := ts;
end;


DL3AD hat geschrieben:Nun habe ich folgendes vor:
Ich habe eine Spalte in der wird das Datenbankfeld "Country" dargestellt.
Nun ist die Zeilenhöhe vergrößert worden so dass nun zwei Zeilen untereinander hineinpassen.
In der oberen Zeile von "Country" soll auch Country ausgegeben werden.
In der Zeile darunter also quasi nach dem Zeilenumbruch möchte ich ein weiteres Datenbankfeld "Info" ineinfrickeln - ich hoffe du hast verstanden was ich meine.

Also irgendwie müsste eine Zeilenumbruch erzeugt werden und nsch dem Zeilenumbruch das zusätzliche Datenbankfeld "Info" angehängt werden.

Eigentlich ist eine Grid-Spalte für ein Feld zuständig. Der sauberste Weg ist wahrscheinlich sich ein berechnetes Feld zu erzeugen, in dem die beiden Felder Country und Info kombiniert werden. Wahrscheinlich geht es aber auch, das Ereignis OnDrawColumnCell zu behandeln und dort die Felder zu kombinieren. Achtung aber: Wenn aus irgendeinem Grund die Spalten enger werden oder in dieser Spalte ebenfalls eine Zeilenumbruch erfolgt, bleiben die Felder kombiniert (gilt für beide Vorschläge).

Hier - ungetestet - eine Anregung für OnDrawColumCell:

Code: Alles auswählen

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  country, info, celltext: String;
begin
  if Column.Field.FieldName = 'Country' then begin
    country := Column.Field.AsString;
    info := TDBGrid(Sender).Datasource.Dataset.FieldByName['Info'].AsString;
    cellText := 'Country: ' + country + LineEnding + 'Info: ' + info;
  end else
    cellText := Column.Field.AsString;
  DBGrid(Sender).Canvas.FillRect(Rect);
  DBGrid(Sender).Canvas.TextRect(Rect, Rect.Left + constCellPadding, Rect.Top + constCellPadding, cellText);
end;

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: DBGrid Multiline ?

Beitrag von DL3AD »

Hallo wp_xyz,

habe nun mein DBGrid fest auf zweizeilig eingestellt und tlTop funktioniert auch.

Allerdings habe ich ein Problem mit dem zusammenbauen des Zellinhaltes - folgendes habe ich gemacht

Code: Alles auswählen

 
//Zelleninhalt zusammenbauen
procedure TForm1.DBGridCLDrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  Country, Info, CellText: string;
begin
  if Column.Field.FieldName = 'Country' then
  begin
    Country:= Column.Field.AsString;
    Info:= TDBGrid(Sender).DataSource.DataSet.FieldByName('Rem').AsString;
    CellText:= Country + sLineBreak + Info;
    Column.Field.AsString:= CellText;//Hier den zusammengebauten Zelltext zuweisen
  end;
end;       
 

Den else Zweig benötige ich nicht - es soll immer zusammengebaut werden.
Nun gibt es eine Fehlermeldung dass die Query nicht im Insert oder edit modus ist. :roll: Ich will ja nix an den Daten Ändern.
Wie erzeugt man ein berechnetes Feld ?

Gruß Frank

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

Re: DBGrid Multiline ?

Beitrag von wp_xyz »

Das geht so nicht. Du darfst nicht Column.Field.AsString:= CellText setzen, denn genau dieses würde einen neuen Text in die Datenbank, und dazu muss vorher .Edit aufgerufen worden sein. Abgesehen davon würde dies eine erneute Ausgabe triggern, wodurch OnDrawColumnCell gleich nochmal aufgerufen wird, etc., bis dem Programm mit einem Stackoverflow die Luft ausgeht.

Nimm einfach meinen Code - ich habe es gerade ausprobiert, das funktioniert. Wenn DefaultDrawing auf seinem Default-Wert true, kann der ELSE-Zweig tatsächlich entfallen, weil die Zellen schon vorher per DefaultDrawCell ausgegeben worden sind und im OnDrawColumNCell-Event einige neu übermalt werden. Wenn du die doppelte Ausgabe (evtl. leichtes Flackern) vermeiden willst, musst du DefaultDrawing auf False setzen und den ELSE-Zweig aktivieren.

Eine berechnetes Feld wird dagegen mit dem Feld-Editor erzeugt: Rechtsklick auf der Dataset-Komponente - "Felder bearbeiten", denn 3. Icon für "Neues Feld" > "Feldtyp" "berechnet" und Feldname und -typ evtl. auch -größe eintragen. Im OnCalcFields-Ereignis dann Code schreiben, wie der Feldinhalt berechnet werden soll. Leider habe ich kein passendes Datenbank-Beispiel parat - es wäre zu aufwendig wäre, das extra zu entwerfen.

DL3AD
Beiträge: 478
Registriert: Fr 13. Sep 2013, 12:07
OS, Lazarus, FPC: Debian Bullseye (L 2.2.0)
CPU-Target: 64Bit
Wohnort: Rügen

Re: DBGrid Multiline ?

Beitrag von DL3AD »

Hallo wp_xyz,

so, es funktioniert nun DANKE.

Kanns du mir bitte noch mal kurz erklären was diese beiden Zeilen machen

Code: Alles auswählen

 
  DBGrid(Sender).Canvas.FillRect(Rect);
  DBGrid(Sender).Canvas.TextRect(Rect, Rect.Left + constCellPadding, Rect.Top + constCellPadding, cellText);
 


Gruß Frank

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

Re: DBGrid Multiline ?

Beitrag von wp_xyz »

Das Grid hat wie viele andere Controls eine "Zeichenfläche" (Canvas) mit einer Vielzahl von Zeichenmöglichkeiten (z.B. http://perthes-gymnasium.de/cms/include ... canvas.pdf)

Canvas.FillRect(Rect) füllt das übergebene Rechteck mit Farbe und Füllmuster, so wie sie im Canvas eingestellt sind (Canvas.Brush.Color, Canvas.Brush.Pattern usw); bevor das OnDrawColumnCell-Ereignis aufgerufen wird, sind diese Eigenschaften bereits vorbesetzt worden, wobei das Ereignis OnPrepareCanvas, das du ja schon verwendest, die letzte Möglichkeit ist, die Voreinstellung nochmals zu ändern (oder natürlich auch direkt im OnDrawColumnCell-Event).

Canvas.DrawRect(Rect, Left, Top, Text) gibt in dem übergebenen Rechteck den angegebenen Text aus, wobei der TextStyle des Canvas berücksichtigt wird, also oben bündig, weil Layout = tlTop, oder mehrzeilig, weil WordBreak = true. Left und Top sind die Startkoordinaten des Textes, wobei diese nicht bei allen Textausrichtungen berücksichtigt werden.

Antworten