Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Zeilen eines TDBGrid "Blockweise" färben (https://www.delphipraxis.net/195018-zeilen-eines-tdbgrid-blockweise-faerben.html)

Jumpy 26. Jan 2018 13:06

Zeilen eines TDBGrid "Blockweise" färben
 
Hallo,

ich weiß, wie man Zeilen eines Grids abwechselnd färbt, aber wie mach ich das Blockweise?

Ich habe ein Grid, wo immer mehrere Zeilen untereinander zu einem Vorgang gehören, jede Zeile hat eine eigene ID und mehrere Zusammen / Hintereinander die selbe VorgangsID. Unterschiedlich viele Zeilen gehören so zusammen.
Dementsprechend ist die Datenmenge sortiert nach "Order by VorgangsID, ID".

Jetzt möchte ich, dass das Grid nicht nach jeder Zeile sondern nach jedem Vorgang die ID wechselt. Aber wie mach ich das?

Was nicht klappt

Delphi-Quellcode:
// im OnOwnerDrawCell
 if GridIDChangeDetected then
    SwitchGridColor;
  SetLastGridID;
// usw.

procedure TCheckdaten.SwitchGridColor;
begin
  if CurrentGridColor=Color1 then
     CurrentGridColor:=Color2
  else
   CurrentGridColor:=Color1;
end;

procedure TCheckdaten.SetLastGridID;
begin
  Last_VG_ID:=Quelle.FieldByName('VG_ID').AsInteger;
end;

function TCheckdaten.GridIDChangeDetected:Boolean;
begin
  Result:=Last_VG_ID<>Quelle.FieldByName('VG_ID').AsInteger;
end;
Irgendwie hab ich naiv angenommen, dass beim Zeichnen des Grids die Query einmal durchlaufen wird und das dann so funzt. Aber dem ist wahrscheinlich nicht so.

Delphi.Narium 26. Jan 2018 13:19

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Das Malen passiert erst bei der Anzeige (nur der sichtbare Bereich) und nicht beim Laden der Daten.

Habe Deine Idee vermutlich noch nicht so ganz verstanden, aber hier müsste irgendwo eine Änderung im Quelltext stattfinden:
Delphi-Quellcode:
procedure TCheckdaten.SwitchGridColor;
begin
  If GridIDChangeDetected then begin
    if CurrentGridColor = Color1 then
       CurrentGridColor := Color2
    else
     CurrentGridColor := Color1;
  end;
end;

Jumpy 26. Jan 2018 13:25

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Wenn ich Zeilenweise Färbe, dann kann ich ja so Dinge machen wie:
Wenn (Query.FieldByName('ID').AsInterger mod 2)=0 dann Farbwechsel.

Das hilft mir ja nicht, denn ich muss die Zeile der jetzt gezeichneten Celle mir der Zeile davor vergleichen, ob sich bei der VG_ID eine Änderunge ergeben hat, dann Farbwechsel.

Wenn beim Zeichnen halt die Query nicht von oben nach unten durchgegangen wird hilft das ganze ja auch nicht.

RSF 26. Jan 2018 14:05

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Ich hoffe ich habe es richtig verstanden.

Ich mache es so:

Delphi-Quellcode:
procedure TRechnungen.DBGrid1DrawColumnCell(Sender: TObject;
  const Rect: TRect; DataCol: Integer; Column: TColumn;
  State: TGridDrawState);
begin
if (gdSelected in State) or (gdFocused in State) then
   DBGrid1.Canvas.Font.Color := clBlue;
 
   if DataModule2.Rechnung.FieldByName('Bezahlt').AsBoolean=True then
   Begin
   DBGrid1.Canvas.Brush.Color := clGray;
   end;
   if DataModule2.Rechnung.FieldByName('Bezahlt').AsBoolean=False then
   Begin
   DBGrid1.Canvas.Brush.Color := clMoneyGreen;
   end;
   DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;

hoika 26. Jan 2018 16:25

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Hallo,
ich würde mir eine Hilfsklasse bauen, z.B. eine StringList.
Die bekommt so viele Einträge wie Grid-Zeilen (DBGrid.Dataet.RowCount)
Du füllst du mit einfach mit passenden Zahlen (0,1, oder sowas) für die einzelnen Blöcke,
und benutzt die StringList dann im OnDrawCell.

Viel einfacher hättest Du mit einem normalen TStringGrid, dort könntest du mit Objects oder eine versteckten Spalte.

Jumpy 29. Jan 2018 08:29

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Hallo,

sorry die späte Meldung, war halt Wochenende, und da bleibt der Rechner aus :-D.

Also prinzipiell funktioniert das schon, wie ich das oben beschrieben habe, ich hatte nur einen Denkfehler und hab die falsche ID verglichen. Das heißt ich kann die Zeilen Blockweise alternierend einfärben, zumindest beim ersten mal!

Das neue Problem ist nämlich jetzt, dass die ausgewählte Zeile (gdSelected in State) wie üblich anders gefärbt ist, gehighlighted. Das ist natürlich für diese Zeile kein Problem.

Aber wenn ich nun in eine andere Zeile klicke, so wird diese nun gehighlighted, aber die alte Zeile muss nun ja wieder neu gezeichnet werden und da fehlt dann halt der Kontext, da eben nur einzelne Zeilen neu gezeichnet werden und nicht das ganze Grid.

Ich müsste vielleicht sowas machen wie OnSelectionChange->Grid.Repaint, aber ich hab da im Grid kein Event für gefunden.

RSF 29. Jan 2018 10:33

AW: Zeilen eines TDBGrid "Blockweise" färben
 
welches Grid Ereignis benutzt du DrawColumnCell ?

Jumpy 29. Jan 2018 11:12

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Zitat:

Zitat von RSF (Beitrag 1392392)
welches Grid Ereignis benutzt du DrawColumnCell ?

Ja genau. Ich habe hier leider eine alte Anwendung mit Datensensitiven-Controls, hier halt das TDBGrid und die Anforderung des Einfärbens kam jetzt neu auf. Ich will halt ungern das ganze Programm umstellen müssen auf TStringGrid o.ä., weil ich dann für diese kleine Änderung fast alles neu machen muss.

RSF 29. Jan 2018 11:35

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Liste der Anhänge anzeigen (Anzahl: 1)
OK, hast du mein Beispiel aus #4 probiert
Delphi-Quellcode:
procedure TRechnungen.DBGrid1DrawColumnCell(Sender: TObject;
  const Rect: TRect; DataCol: Integer; Column: TColumn;
  State: TGridDrawState);
begin
if (gdSelected in State) or (gdFocused in State) then
   DBGrid1.Canvas.Font.Color := clBlue;
 
   if DataModule2.Rechnung.FieldByName('Bezahlt').AsBoolean=True then
   Begin
   DBGrid1.Canvas.Brush.Color := clGray;
   end;
   if DataModule2.Rechnung.FieldByName('Bezahlt').AsBoolean=False then
   Begin
   DBGrid1.Canvas.Brush.Color := clMoneyGreen;
   end;
   DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
So färbe ich Zeilen. Die Sortierung (Gruppen) macht ja eigentlich ein SQL-Script.
Das geht nicht nur einmal am Anfang auch beim durchklicken.

Delphi.Narium 29. Jan 2018 11:44

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Hast Du für das Programm zufällig die JVCL zur Verfügung?

Dann könnte sich ein Blick auf TJvDBGrid lohnen.
Der Austausch ist einfach:
Altes Grid raus, neues Grid rein, solange der Name gleich bleibt, sollte das erstmal ohne Nebenwirkungen gehen.

Die Methoden für die farblische Gestaltung erscheinen mir aber deutlich leistungsfähiger.

Ein Grundproblem wird aber bleiben:

Beim Scrollen durch die Datenmenge, der Auswahl einer Zeile bzw. dem Aufheben der Auswahl ... muss man immer auf den Satz davor schauen, um auf die richtige Farbgestaltung zu kommen.

Das wird vermutlich zu einem wilden "Gehopse" mit Dataset.Next und Dataset.Prev führen.

Hab's gestern mal versucht, mir ist da keine sinnvolle und immer funktionierende Lösung eingefallen.

Beim Öffnen der Datenmenge und dem vorwärtsscrollen hat's ja noch geklappt, aber rückwärts oder bei der Auswahl eines Datensatzes und folgendem Wechsel vorwärts oder rückwärts bin ich gescheitert.

Was man eventuell noch versuchen könnte:

Neben dem DBGrid noch ein unsichtbares Stringgrid "mitführen".

Beim Lesen des Daten wird in das StringGrid je Datensatz dessen RecNo und seine Farbe als Zeile eingefügt. Scrollt man nun durch die Datenmenge, so schaut man dort nach, ob die RecNo schon vorhanden ist. Wenn ja, wird dem StringGrid der Wert für die Farbe entnommen, andernfalls muss die Farbe ermittelt werden und dann im StringGrid eine entsprechende Zeile eingefügt werden.

Oder immer dann, wenn eine neue VorgangsID auftaucht wird die im StringGrid mit der entsprechenden Farbe vermerkt. Bei einem Datensatzwechsel wird dort dann nachgeschaut, welche Farbe zu vergeben ist. Das könnte im Ereignis OnDrawColumnCell realisierbar sein.

Bei kleinen Datenmengen mag das insgesamt so angehen, für große Datenmengen dürfte es aber eher ungeeignet sein.

Oder im DataSet eine zusätzliche Spalte einfügen für den Farbwert. Die Spalte darf halt nur nicht angezeigt werden. Dann könnte man beim Scrollen die Farbe dort entnehmen. Nur bei der erstmaligen Anzeige eines Datensatzes muss halt die Farbe erst aus dem vorherigen Datensatz ermittelt werden und dann im DataSet eingefügt werden.
Bei einer ReadOnly-Datenmenge dürfte das aber auch schon wieder scheitern.

Uwe Raabe 29. Jan 2018 11:52

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Zitat:

Zitat von RSF (Beitrag 1392401)
OK, hast du mein Beispiel aus #4 probiert

Im Unterschied zu Ralph's Anforderung lässt sich dein Status direkt aus dem aktuellen Datensatz ermitteln. Bei der Frage geht es aber darum, alle Datensätze mit gleicher ID auch gleich zu färben und zusätzlich bei jedem Wechsel der ID diese Farbe umzuschalten. Das lässt sich allein aus dem aktuellen Datensatz nicht ermitteln.

@Ralph: Welche Datenzugriffskomponente setzt du denn ein?

joachimd 29. Jan 2018 11:57

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Pack doch ein berechnetes Feld rein, welches den Status "leer bzw 0" (also Farbe ermitteln), "1" (Farbe 1) und "2" (Farbe 2) hat. Dann setze die Farbe anhand dieses Flags.

Delphi.Narium 29. Jan 2018 12:01

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Für die Berechnung sind aber Informationen aus dem vorherigen Datensatz (VorgangsID) erforderlich.
Berechnet Felder beziehen sich aber (soweit ich weiß) auf den aktuellen Datensatz. Der Datensatz enthält also bereits alle zur Berechnung erforderlichen Informationen. Diese Bedingung ist hier aber nicht erfüllt.

Jumpy 29. Jan 2018 12:25

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1392406)
@Ralph: Welche Datenzugriffskomponente setzt du denn ein?

Da ist eine TADOQuery dahinter, dann TDatasource, dann das TDBGrid.


@all: Danke für die bisherigen Vorschläge. Das Problem hat Uwe gut beschrieben, nicht der aktuelle Datensatz alleine bestimmt ob/wie gefärbt wird.

Ich überlege jetzt (nach dem ersten Zeichnen) eine Event-Prozedur an das OnDataChange der TDatasource zu hängen. Feuert dieses, dann
- weise dem OnDataChange nil zu
- Komplettes Grid.Repaint
- dann weise dem OnDataChange wieder die Prozedur zu
(da das OnDataChange während eines Repaint ohne Ende feuert, muss man das mMn vorher ausschalten).

Komme aber erst später dazu das mal zu testen, da ich gerade nicht in der Firma bin.

RSF 29. Jan 2018 12:43

AW: Zeilen eines TDBGrid "Blockweise" färben
 
@Uwe Raabe Danke für die verständliche Erklärung.
Könnte es eventuell mit einer temporären Tabelle(im dbGrid) mit den erforderlichen Flags gehen. Die entsprechend mit den veränderten IDs aktualisiert wird.
Ich meine damit das Problem auf DB-Ebene zu lösen.
Oder ist der Gedankengang wieder daneben.:oops:

Uwe Raabe 29. Jan 2018 13:33

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Zitat:

Zitat von Jumpy (Beitrag 1392413)
Da ist eine TADOQuery dahinter, dann TDatasource, dann das TDBGrid.

Ist die Query Read-Only und passt komplett in den Speicher?

In dem Fall könntest du die Daten in ein TClientDataSet oder ein anderes InMemory-Dataset übernehmen und dabei ein zusätzliches Feld für die Farbe besetzen. Da die Übernahme sequentiell erfolgt, kann der ursprüngliche Algorithmus für das Färben verwendet werden.

Das funktioniert natürlich nicht so gut, wenn die Daten im Grid verändert werden. In dem Fall kannst du aber vielleicht ein temporäre Tabelle anlegen und diese als Lookup verwenden. Die Tabelle hat nur zwei Felder (ID, Farbe), wobei ID mit der ID der Originaltabelle identisch ist.

Wenn die Query eine überschaubare Datenmenge zurückliefert, kannst du nach dem Öffnen auch einfach ein Dictionary (ID, Farbe) anlegen. Es läuft darauf hinaus, daß du irgendwie für jeden Datensatz eine Farbe hinterlegst.

Jumpy 29. Jan 2018 13:54

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Hallo Uwe,

jetzt hab ich Stoff zum nachdenken. Die Daten an sich können im Grid nicht verändert werden, aber sie können gefiltert oder sortiert werden, wodurch sich die Reihenfolge ändert. Ich muss mal schauen, ob man nach jeder Fileraktion oder so in zusätzlichen Feldern eines ClientDatasets (wenn ich denn darauf umstelle) die Farbwerte neu vergeben kann.

jobo 29. Jan 2018 14:25

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Zitat:

Zitat von Jumpy (Beitrag 1392425)
..
jetzt hab ich Stoff zum nachdenken.

Ich habs nur überflogen, also ggF. nicht ernst nehmen.
Gemäß Uwe sind zwar bei der Färbung einer Gruppe mehrere Datensätze relevant für die Frage ob gleiche Gruppe oder nächste. Das kann ich aber immer mit einem Merker auf den vorigen Datensatz abfangen oder? Ich muss nicht wissen, was alles kommt. Ich muss nur merken, Wert ändert sich also wechsel ich die Farbe.

Dies gilt natürlich nur, wenn die Darstellung gemäß initialer Abfrage/Sortierfolge erfolgt.

Dann filtern/sortieren:
Hier wäre es erstmal eine Frage, was ich erreichen will.
a) Gruppen/Färbung beibehalten (wie original): Visueller Effekt wäre dann, "Flatterfärbung" oder scheinbare Gruppen, obwohl durch Filterung einfach nur andere fehlen und bei Hinundher Farbwechsel dann scheinbar neue Gruppen entstehen.
Der User würde das ggF. als Indikator für "unvollständige " Gruppen sehen/bemerken. Vielleicht deutlicher/besser, wenn mit hinreichend vielen Farben (mehr als 2 ) gearbeitet wird.
b) ich will den "Stile" beibehalten und wende die Färbungsmechanik neu an, bei geänderter Sortierung vorzugsweise auf das neue Sortierfeld.

hoika 29. Jan 2018 14:49

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Hallo,
Zitat:

Ich muss nur merken, Wert ändert sich also wechsel ich die Farbe.
Das sehe ich aber im OnDrawCell von Zeile2 nicht, dass die vorige Zeile eine andere Id hatte.

Jumpy 29. Jan 2018 16:22

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Zitat:

Zitat von hoika (Beitrag 1392427)
Hallo,
Zitat:

Ich muss nur merken, Wert ändert sich also wechsel ich die Farbe.
Das sehe ich aber im OnDrawCell von Zeile2 nicht, dass die vorige Zeile eine andere Id hatte.

Genau das ist halt mein Problem. Deswegen merke ich mir die ID der vorherigen Zeile um zu sehen, ob ein Wechsel stattfiden muss. Das funktioniert dann aber nur beim erstmaligen Zeichnen des Grids, wo das von oben nach unten neu gezeichnet wird und dementsprechend die darunterliegende Query einmal von oben nach unten durchlaufen wird.
Wenn ich den Cursor/Selector zwischen den Zeilen hin und her springe, werden nur einzelne Zeilen neu gezeichnet und ich habe nicht den Bezug zu den Zeilen davor. Genau das ist mein Problem.

@Jobo: Sortierung war quatsch, das Grid kann nicht sortiert werden, denn es muss ja immer ala "Order By GruppenID, EinzelID" sortiert sein, damit es überhaupt erst möglich ist zusammenhängende Blöcke zu haben.

Das einzige was passieren kann ist Filterung, d.h. die Datenmenge ändert sich und das Grid müsste eigentlich komplett neu gezeichnet und gefärbt werden. Das eine weiße Zeile dann grau wird oder umgekehrt ist egal. Es geht ja nur darum den einen Block vom nächsten zu unterscheiden. Und deswegen kann dann der Übernächste Block auch wieder die selbe Farbe haben wie der erste.

Ich basteln nachher in der Firma nochmal ein bisschen rum, irendwie muss das doch gehen :).

Delphi.Narium 29. Jan 2018 16:44

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Beim Zeichnen der Daten müsste das Grid doch eigentlich immer erstmal von oben nach unten durch die Datenmenge gehen.

Wenn man nun hergeht und im Ereignis AfterOpen (bzw. beim Setzen eines Filters) eine (globale) Stringliste leert und immer dann, wenn eine Zeile gezeichnet wird prüft, ob die aktuelle VorgangsID dort vorhanden ist, müsste doch ungefähr so eine Logik funktionieren:
Delphi-Quellcode:
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  if StringList.IndexOf(VorgangsID) < 0 then StringList.Add(VorgangsID);
  Case StringList.IndexOf(VorgangsID) Mod 2 of
    0 : DBGrid1.Canvas.Brush.Color := clWhite;
    1 : DBGrid1.Canvas.Brush.Color := clInfoBk;
  end;
end;

HolgerX 29. Jan 2018 16:47

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Hmm..

Ich würde mir ne StringList nehmen und dort nach dem Name/Value Prinzip Einträge erzeugen.

Die Liste wird beim OnAfterOpen des Querys gelöscht.

Beim OnDrawCell wird nun in der Liste nach einem 'Name' mit der ID gesucht (StringList.NameOf()).
- Wenn Nicht vorhanden, dann ein Add mit ID=NewColor hinzufügen.
- Wenn Vorhanden, dann die Color (StringList.Value[]) der ID verwenden.

Wenn nun gescrollt wird, wird immer die passende Farbe zur ID verwendet, egal ob rauf oder runter.

Dann ist auch die Sortierung der Datenmenge egal, da die Farbe zur ID des Datensatzes geholt wird.

Wenn Du die StringList auf Sorted setzt, wird auch das Suchen darin schneller.

(Nur so eine Idee zur Umsetzung ;) )

Uwe Raabe 29. Jan 2018 16:51

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Es würde auch reichen, wenn du dir die Farbe pro VorgangsID irgendwo hinterlegst.

Jumpy 29. Jan 2018 16:54

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Das klingt sehr gut. Beim erstmaligen Zeichnen wird die Farbinformation in einer Parallelstruktur gespeichert und über die VorgangsID verlinkt. Das kann dann beim Neuzeichnen einzelner Zeilen oder Zellen weiterhin benutzt werden.
Erst bei einer Filterung und somit einen anderen Aufbau des Grids muss diese Parallelstruktur aktualisiert werden.

Ich glaube das habt ihr weiter oben auch schon mal gemeint, als z.B. Uwe von temporärer Tabelle o.ä. gesprochen hat. Da hab ich nicht verstanden was gemeint war, aber jetzt scheint es mir klar zu sein.

Werde ich morgen gleich testen. Irgendwie will die Bahn mich heute nicht mehr in die Firma lassen :cry:

Uwe Raabe 29. Jan 2018 17:41

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Ganz simpler Ansatz:
Beim Einlesen der Daten erstellst du eine Liste der VG_IDs (z.B:
Delphi-Quellcode:
TList<Integer>
). Dazu musst du eventuell einmal durch die ganze Query durch, da das Grid eventuell nur einen Teil der Daten abruft, die in seinen internen Puffer passen.
Im DrawCell-Event hast du ja für den aktuellen Datensatz die VG_ID in dem entsprechenden Feld. Nun suchst du den Index dieser VG_ID in der zuvor erstellten Liste. Je nachdem ob der Index gerade oder ungerade ist (kann man leicht mit der Funktion
Delphi-Quellcode:
Odd()
prüfen) malst du die Zeile in der einen oder anderen Farbe.

Jumpy 30. Jan 2018 09:31

AW: Zeilen eines TDBGrid "Blockweise" färben
 
Hallo und guten morgen,

habe jetzt erstmal den Ansatz von Delphi.Narium mit der Stringliste umgesetzt (@Uwe: Dieses Programm ist noch in D6 ohne Generics, sonst wäre dein Vorschlag noch einfacher) als einfachste Version einer Liste, arbeite halt einfach mit der ID als String. Funzt sehr gut.
Auch das Aktualisieren / Neuzeichnen nach Filter-Aktionen klappt schon, oder wenn wein modales Form auf und wieder zugeht, müssen ja die davon verdeckten Zellen neue gezeichnet werden, klappt alles.

Bei manchen Aktionen kommt es noch durcheinander, aber da muss ich nur noch die Stellen finden, an denen ich die Stringlist zurücksetzen muss, damit das da auch klappt, aber das krieg ich hin.

Vielen lieben Dank allen Helfenden :thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:38 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz