Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Spalten in einem DBGrid verschieben und auch sortieren! (https://www.delphipraxis.net/870-spalten-einem-dbgrid-verschieben-und-auch-sortieren.html)

m-werk 17. Sep 2002 20:19


Spalten in einem DBGrid verschieben und auch sortieren!
 
Ich hab da ein kleines problem:

Ich habe im Formular unter 'type' folgendes eingegeben:
Code:
THackAccess = class(TCustomGrid);
und weiters

Code:
procedure MoveDBGrid1Columns(DBGrid1: TDBGrid; FromColumn, ToColumn: Integer);
begin
  THackAccess(DBGrid1).MoveColumn(FromColumn, ToColumn);
end;
Wenn ich das Prog. jetzt starten möchte, kommt eine Fehlermeldung:

':'erwartet, '=' gefunden

Das ist bei der Zeile unter Type.

Was mach ich da falsch?
Ich möchte, dass der User die Colums im DBGrid verschieben kann, wie er möchte.

Weiters hab ich noch ne frage:
In den Colums werden Datensätze wie z.B. Name Vorname usw angezeigt.
Wie kann ich es erstellen, dass wenn ein User auf eine Columsüberschrift klickt, die Daten entweder von A-Z oder von Z-A sortiert werden. (Dies soll für jede columne so sein!)

d3g 17. Sep 2002 20:28

Hi m-werk,

irgendwie muss das was mit der type-Deklaration faul sein. Poste mal die Deklaration vor THackAccess, ich vermute, das fehlt ein end oder etwas ähnliches.

MfG,
d3g

m-werk 17. Sep 2002 20:40

Die Deklaration hab ich ja eh schon gepostet! oder nicht?

Ich habe unter type das stehen:

Code:
THackAccess = class(TCustomGrid);
und noch den code der procedure, den ich vorher hineingeschrieben habe.

Hab ich da was vergessen?

d3g 17. Sep 2002 20:56

Hi m-werk,

Zitat:

Poste mal die Deklaration vor THackAccess
MfG,
d3g

m-werk 17. Sep 2002 21:02

Code:
type
  TKundenDruckMenue = class(TForm)
  THackAccess = class(TCustomGrid)
    Button2: TButton;
    DBGrid1: TDBGrid;
    Label1: TLabel;
    .... usw.

d3g 17. Sep 2002 21:11

Hi m-werk,

so muss es heißen:

Code:
type
  TKundenDruckMenue = class(TForm)
    Button2: TButton;
    DBGrid1: TDBGrid;
    Label1: TLabel;
    .... usw.
  end;

  THackAccess = class(TCustomGrid);
Evtl. THackAccess vor TKundenDruckMenue stellen.

MfG,
d3g

m-werk 17. Sep 2002 21:19

DANKE, das mit dem Code nach dem end. funktioniert spitze. Ich kann jetzt die Spalten bewegen, wie ich möchte.

Eine sache habe ich dabei noch bemerkt. Wenn ich jetzt die Spalten vertausche, und dann das programm nochmal starte, ist alles wieder so, wie es vorher war. Das vertauschen wird nicht gespeichert. Wie kann ich dass den lösen.

Weiters:

Wie kann ich jetzt noch sortieren?
Wenn ich auf eine Spaltenüberschrift klicke, dann soll sortiert werden. Entweder von A-Z oder von Z-A.

Kannst du mir da auch noch helfen?

d3g 17. Sep 2002 21:22

Sorry, ich benutze im Moment nur D6 Personal und das siehts mit TDBGrid ein bisschen mager aus...

MfG,
d3g

Luckie 17. Sep 2002 21:23

Das DBGrid dient doch nur der Darstellung der Daten, das heißt die Sortierung müßte IMHO mit einer Abfrage gemacht werden.

m-werk 17. Sep 2002 21:27

IMHO? was bedeutet das?

Wie könnte denn so eine Abfrage aussehen?
Wie kann ich auch die Spaltenverschiebung speichern, so dass beim nächsten Start des Prog. nicht wieder die urspüngliche Einstellung angezeigt wird?

d3g 17. Sep 2002 21:38

Hi m-werk,

IMHO: in my humble opinion.

Was das Speichern angeht, könntest du ja die Property Cols[] abspeichern und neu laden.

MfG,
d3g

Luckie 17. Sep 2002 21:39

IMHO = in my humble opinion = in meiner bescheiden Meinung

Keine Ahnung wie so eine Abfrage auszusehen hat. Von Datenbanken habe ich keinen Schimmer. Ich weiß nur so viel, dass das DBGrid nicht dazu da ist Daten zu manipulieren, zu filtern etc., dazu gehört auch das Sortieren. Bitte korrigieren, wenn ich falsch liegen sollte, aber ich bin mir hier ziemlich sicher.

Die Abfrage hängt auch von der Datenbank ab: mySQL, BDE, Paradox. Access, ...

d3g 17. Sep 2002 21:42

Aua, es handelt sich, um ein DB- kein StringGrid.
Bitte mein obiges Posting vergessen...

MfG,
d3g

MrSpock 18. Sep 2002 06:56

Hallo m-werk,

zunächst einmal zum Thema Sortierung:

Es gibt da grundsätzlich zwei Möglichkeiten. Die erste ist das Arbeiten mit Indices. Dazu legst du für alle möglichen Sortierungen Sekundärindices an. Dann wählst du einfach den gewünschten Index aus und die Sortierung funktioniert automatisch.

Die zweite Möglichkeit ist eine Abfrage zu erstellen in der Form:

Code:
SELECT * FROM Kunde ORDER BY Vorname DESC
Diese Abfrage packst du in eine TQuery Kompo und führst diese durch "Open" aus.

Zum Thema "Speichern der Reihenfolge der Felder". Dazu würde ich die Eigenschaft "Index" der TField Objekte auslesen und in einer Ini-Datei speichern. Beim Starten der Anwednung liest du diese Werte wieder aus und weist sie der Index-Eigenschaft wieder zu.

m-werk 18. Sep 2002 10:35

Danke für den Tip beim Sortieren. Ich werde die 2. Variante nehmen.

Mit dem Speichern der Spaltenpositionen komm ich nicht so zurecht. Kannst du mir dabei bitte helfen?

MrSpock 18. Sep 2002 11:54

Hallo m-werk,

sowohl TQuery als auch TTable sind (indirekt) von TDataSet abgeleitet. Dort ist die Eigenschaft Fields definiert.

Je nachdem wann du die Reihenfolge der Felder im Grid speichern willst, könntest du in der entsprechenden Ereignismethode folgendes schreiben:

Code:
var
  Ini        : TIniFile;
  i          : Integer;
  reihenfolge : String;
 
...
try
  Ini := TIniFile.Create(ChangeFileExt(Application.ExeName), '.INI');
  reihenfolge := '';
  for i := 0 to MyTable.FieldCount-1 do
    reihenfolge := reihenfolge+IntToStr(MyTable.Fields[i].Index)+';'

  Ini.WriteString('Grid', 'Reihenfolge', reihenfolge);
  Ini.Free;
except
...
Das Einlesen läuft analog, z.B. in der OnActivate Methode.

Ist zwar nicht getestet, sollte aber funktionieren.

m-werk 20. Sep 2002 17:58

OK, das mit dem Speichern habe ich jetzt hinbekommen. Funktioniert tatellos.

Es ist nur noch ein Problem:
Im DBGrid müssen erst Datensätze stehen, dannach kann ich die Spalten verschieben und die Position wird dann erst in der INI gespeichert.
Vielleicht könnte man das auch dann machen, wenn keine Daten im Grid stehen.

Aber nun zum nächsten.
Wie rufe ich die Position der Spalten beim öffnen des Formulares wieder auf, die in der INI stehen?

Luckie 20. Sep 2002 18:22

Sollte eigentlich. Aber dein gesamter try-Block ist Mist. Bei einem Fehler wird Ini nicht wieder freigegeben usw.

So ist es besser:

Code:
[b]procedure[/b] TKundenDruckMenue.FormClose(Sender: TObject;
[b]var[/b] Action: TCloseAction);
[b]var[/b]
  Ini : TIniFile;
  i : Integer;
  reihenfolge : [b]String[/b];
[b]begin[/b]
  reihenfolge := '';
  [b]for[/b] i := 0 [b]to[/b] DBGrid1.FieldCount-1 [b]do[/b]
    reihenfolge := reihenfolge+IntToStr(DBGrid1.Fields[i].Index)+';';
  Ini:=TIniFile.Create(ExtractFilePath(Application.ExeName)+ 'Einstellungen.ini');
  [b]try[/b]
    Ini.WriteString('Grid', 'Reihenfolge', reihenfolge);
  [b]finally[/b]
    Ini.Free;
  [b]end[/b];
[b]end[/b];
Eventuell behebt das auch dein Poblem.

m-werk 20. Sep 2002 18:29

Danke für die verbesserung, aber das Problem ist damit nicht behoben!

Luckie 20. Sep 2002 18:34

Wärend ich gepostet habe, hast du doch geschrieben, dass es jetzt gehen würde oder was oder wie oder wo oder...

m-werk 20. Sep 2002 18:37

Ja sicher funktioniert es, aber nur dann wenn Daten im Grid stehen. Kann man das auch irgendwie lösen, auch wenn keine Daten im Grid stehen?

Das ist aber nicht so wichtig. Wichtig wäre Wie kann ich jetzt die Spaltenposition beim öffnen des Formulares aus der INI lesen?

MrSpock 20. Sep 2002 18:55

Hallo m-werk,

was genau meinst du mit

Zitat:

Im DBGrid müssen erst Datensätze stehen
Meinst du, dass die Tabelle erst geöffnet werden muss oder dass das Verschieben der Spalten mit einer leeren Tabelle nicht funktioniert?

Nun zum Lesen der IniDatei:

Code:
var
  Ini : TIniFile;
  i,
  posi : Integer;
  reihenfolge : String;
begin
  reihenfolge := '';
  try
    Ini:=TIniFile.Create(ExtractFilePath(Application.ExeName)+ 'Einstellungen.ini');
    reihenfolge := Ini.ReadString('Grid', 'Reihenfolge', '');
  finally
    Ini.Free;
  end;
  try
    posi := Pos(reihenfolge, ';');
    i := 0;
    while posi > 0 do
    begin
      DBGrid1.Fields[i].Index := StrToInt(Copy(reihenfolge, 1, posi-1));
      Delete(reihenfolg, 1, posi);
      posi := Pos(reihenfolge, ';');
      Inc(i);
    end;
   except
     on E:exception do
        ShowMessage('Fehler in der Ini-Datei:'#13
                         +E.Message);
   end;
end;
Hab ich zwar nicht getestet, aber so ähnlich sollte das wohl aussehen.

m-werk 20. Sep 2002 19:00

Zitat:

Meinst du, dass die Tabelle erst geöffnet werden muss oder dass das Verschieben der Spalten mit einer leeren Tabelle nicht funktioniert?
Genau das meine ich. Das Verschieben der Spalten bei einer Leeren Tabelle wird nicht in die INI gespeichert.

Danke für den Code, ich werde ihn am Wochenende testen.

Luckie 20. Sep 2002 19:10

Herr im Himmel. :evil:

Nehmt doch endlich mal das TIniFiel.Create aus dem try-Block raus!

Lest ihr euch denn nicht die Warnungen durch, die der Kompiler ausgibt? :roll: Wenn ich das so mache wie ihr, steht da immer:
Zitat:

[Warnung] Unit1.pas(35): Variable 'ini' wurde wahrscheinlich nicht Initialisiert
Das habe ich schon öfters bemängeld und korrigiert.

Hier noch mal wie es richtig geht:
Code:
[b]procedure[/b] TForm1.Button1Click(Sender: TObject);
[b]var[/b]
  ini: TiniFile;
[b]begin[/b]
  ini := Tinifile.Create(ChangeFileExt(ParamStr(0), '.ini'));
  [b]try[/b]
    ini.WriteString('Programm', 'Pfad', ParamStr(0));
  [b]finally[/b]
    ini.Free;
  [b]end[/b];
[b]end[/b];

MrSpock 20. Sep 2002 19:19

Jaja, :oops: is ja schon gut... :mrgreen:

Luckie 20. Sep 2002 19:22

Na dann wollen wir mal hoffen, dass das jetzt angekommen ist. Wenn ich das noch mal bei dir sehen, dann schreibst du mir hundertmal:
Zitat:

TIni.Create kommt nicht in den try-Block.

MrSpock 20. Sep 2002 19:26

@Luckie: schon mal profilaktisch...

Code:
for i := 1 to 100 do
  Writeln('TIni.Create kommt nicht in den try-Block. ');
:shock:

MrSpock 20. Sep 2002 19:50

@m-werk,

ich hab mir das jetzt mal angeschaut, es funktioniert auch mit leeren Tabellen, aber nicht mit GESCHLOSSENEN Tabellen. Das ist ja auch klar, weil dann die Verbindung zur Tabelle nicht besteht. Du musst also sicher stellen, dass die Tabelle geöffnet ist, wenn die Reihenfolge geschrieben werden soll. Auch beim Lesen und zuweisen der Indices muss die Tabelle geöffnet sein.

m-werk 23. Sep 2002 16:29

Hi, was soll ich den genau tun, damit die Tabelle offen ist?

MrSpock 23. Sep 2002 19:20

Hallo m-werk,

grundsätzlich natürlich mit MyTable.Open. Irgendwo wird ja die Tabelle geöffnet, entweder schon im Objektinspektor oder irgendwo im Programm. Dann lässt du einfach das Schließen der Tabelle nicht zu.

m-werk 24. Sep 2002 07:17

Hi, das DBGrid greift auf eine ADOQuery zu. Ich habe in der ADOQuery einen SQL-String: GROUP BY KundenNr. Diesen brauche ich, da ich ja einige Abfragen in dem Formular habe, die dann in dem Grid ersichtlich sind. Wenn ich das Formular öffne, dann ist das Grid leer. Das soll auch so sein, da dieses Formular nur zum Filtern von Daten gedacht ist.

Ich hab aber auch schon probiert, ADOQuery.Open. Da muß ich aber zuerst eine SQL wie z.B. SELECT, INSERT usw. einfügen.

Wenn ich so etwas einfüge, dann sehe ich jetzt beim öffnen Alle Kunden im Grid. (Zeichen dafür, dass die Tabelle offen ist.) Aber das mit dem Lesen der INI, wo ich dann die Reihenfolge drinn habe, funktioniert nicht.

Das Speichern geht super.
Kann man das so lösen, dass die Tabelle offen ist, wenn ich das Formular öffne, ohne dass irgendwelche Daten im Grid stehen?

m-werk 30. Sep 2002 16:30

Hi Leute, hat keiner von euch eine Idee, wie Ich das obrige Problem lösen kann?

Wäre euch zu dank verpflichtet, wenn ihr mir helfen könnt!

MrSpock 30. Sep 2002 20:24

Hallo m-werk,

ich könnte mir vorstellen, dass du ein SELECT Statement benutzt, dass die korrekte Struktur, also alle die Felder enthält, die normalerweise angezeigt werden sollen, erzeugt, aber keine Datensätze enthält. Z.B.: SELECT * FROM Personen WHERE ID = 0, wobei eine ID 0 nicht existiert. So erhälst du eine leere Datenmenge. Probier das doch mal aus.

m-werk 1. Okt 2002 19:00

Hi, hab nicht ganz kapiert, was du meinst.

Für die Anzeige im DbGrid hab ich folgenden Code zusammengeschnipselt:

Code:
procedure TKundenDruckMenue.Button6Click(Sender: TObject);
var
  cSql : String;
begin
  with ADOQuery1 do begin
    Active := False;
    Sql.Clear;
    if CheckBox1.Checked then begin
    cSql := 'Select DISTINCT KundenNr, Anrede, Titel, Vorname, Nachname, Straße, PLZ, Ort, Geburtsdatum, Land, Beruf, Telefon, Mobil, Fax, email FROM Kundendaten';
    end else
    if (allekunden.Checked = False) and (email.Checked = False) and (lv.Checked = False) and (flv.Checked = False) and (df.Checked = False) and (ff.Checked = False) and (sv.Checked = False) and (bsp.Checked = False) and (sto.Checked = False) and (mk.Checked = False) then
    begin
      if Trim(Edit1.Text) = '' then begin
      ShowMessage('Bitte geben Sie einen Wert ein!');
      exit;
    end;
    cSql := 'Select DISTINCT KundenNr, Anrede, Titel, Vorname, Nachname, Straße, PLZ, Ort, Geburtsdatum, Land, Beruf, Telefon, Mobil, Fax, email FROM Kundendaten where (' + ComboBox1.Text + ' like ' + QuotedStr (Edit1.Text + '%') +')'
    end
    else
      cSql := 'Select DISTINCT A.KundenNr, A.Anrede, A.Titel, A.Vorname, A.Nachname, A.Straße, A.PLZ, A.Ort, A.Geburtsdatum, A.Land, A.Beruf, A.Telefon, A.Mobil, A.Fax, A.email FROM Kundendaten A LEFT JOIN Geschaeftsdaten B ON A.KundenNr = B.KundenNr ';
    if (allekunden.Checked) and (flv.Checked) and (lv.Checked) and (df.Checked) and (ff.Checked) and (sv.Checked) and (bsp.Checked) and (sto.Checked) then
        cSql := cSql + ' AND ( [A.ist Kunde] = True OR [B.flv] = True OR [B.lv] = True OR [B.df] = True OR [B.ff] = True OR [B.sv] = True OR [B.bsp] = True OR [B.sto] = True OR [A.email] = True OR [B.eigengeschaeft] = True)'
    else if allekunden.Checked then
      cSql := cSql + ' WHERE A.[ist Kunde] = True'
    else if flv.Checked then
      cSql := cSql + ' WHERE B.[flv] = True'
    else if lv.Checked then
      cSql := cSql + ' WHERE B.[lv] = True'
    else if df.Checked then
      cSql := cSql + ' WHERE B.[df] = True'
    else if ff.Checked then
      cSql := cSql + ' WHERE B.[ff] = True'
    else if sv.Checked then
      cSql := cSql + ' WHERE B.[sv] = True'
    else if bsp.Checked then
      cSql := cSql + ' WHERE B.[bsp] = True'
    else if sto.Checked then
      cSql := cSql + ' WHERE B.[sto] = True'
    else if mk.Checked then
      cSql := cSql + ' WHERE B.[eigengeschaeft] = True';
    if email.Checked then
      cSql := cSql + ' WHERE TRIM (A.[email]) <> ' + QuotedStr ('');
    Sql.Add(cSql);
    Open;
  end;
end;
Kannst du damit was anfangen?

Hansa 1. Okt 2002 19:25

Hi M-werk,

habe das Thema gerade gesehen, bin an etwas weit entfent ähnlichem dran.

Code:
WHERE A.[ist Kunde]
Was sind denn das für [] ?? Ist doch SQL oder hab ich mich verlesen ? Der Code sieht jedenfalls ziemlich kompliziert aus. Kannst Du nicht das ewig lange IF..THEN..ELSE irgendwie in logisch zusammenhängende von mir aus auch auf 2 oder 3 Case Blöcke verteilen ? Zur Fehlersuche möchte ich den Code nicht. :mrgreen:

Vielleicht druck ich mirs mal aus und schau genauer. Aber erklär mir mal das mit den [] und das was da drin steht.

Gruß
Hansa

m-werk 1. Okt 2002 20:13

Bei dieser Abfrage geht es um folgendes.

Ich habe in der Access-DB einige Tabellen.
Ich habe hier eine Abfrage, die aus 2 Tabellen besteht.

Code:
cSql := 'Select DISTINCT A.KundenNr, A.Anrede, A.Titel, A.Vorname, A.Nachname, A.Straße, A.PLZ, A.Ort, A.Geburtsdatum, A.Land, A.Beruf, A.Telefon, A.Mobil, A.Fax, A.email FROM Kundendaten A LEFT JOIN Geschaeftsdaten B ON A.KundenNr = B.KundenNr ';
    if (allekunden.Checked) and (flv.Checked) and (lv.Checked) and (df.Checked) and (ff.Checked) and (sv.Checked) and (bsp.Checked) and (sto.Checked) then
        cSql := cSql + ' AND ( [A.ist Kunde] = True OR [B.flv] = True OR [B.lv] = True OR [B.df] = True OR [B.ff] = True OR [B.sv] = True OR [B.bsp] = True OR [B.sto] = True OR [A.email] = True OR [B.eigengeschaeft] = True)'.....
Tabelle A = Kundendaten und Tabelle B = Geschäftsdaten.

Das A weist auf eine Tabelle hin und [ist Kunde] = Feld in der Tabelle.

Ich habe für diese Abfrage ca 4 Wochen mit hilfe eines Freundes gebraucht.

Diese Funktioniert aber jetzt einwandfrei.

MrSpock 2. Okt 2002 07:01

Hallo m-werk,

was ich meinte ist, wenn du z.B. folgendes SQL Statement erzeugst:

Code:
Select DISTINCT A.KundenNr, A.Anrede, A.Titel, A.Vorname, A.Nachname, A.Straße, A.PLZ, A.Ort, A.Geburtsdatum, A.Land, A.Beruf, A.Telefon, A.Mobil, A.Fax, A.email FROM Kundendaten WHERE A.KundenNr = 0
dann (vorausgesetzt es gibt keine Kundenummer 0) sollte eine leere Datenmenge erzeugt werden, die die gewünschte Struktur hat. Wenn du diese dann (solange bis du eine echte Abfrage startest) im Grid anzeigst, sollte das Verschieben der Spalten und das Speichern in der Ini-Datei funktionieren.

Boxma 2. Okt 2002 10:37

Das speichern der Columns sollte auch mit

DBGrid1.Columns.SaveToFile('c:\sortierung.txt');

und

DBGrid1.Columns.LoadFromFile('c:\sortierung.txt');

funktionieren :witch:

waldforest 26. Jun 2005 17:45

Re: Spalten in einem DBGrid verschieben und auch sortieren!
 
Halo,
habe den Code zum einlesen der Spalten einmal ausprobiert.
Leider werden die Spaltensortierung eingelesen, aber hat keine Auswirkung auf die Anzeige.
Ich habe auch keine Möglichkeit gefunden die Anzeige zu refreshen.

Der Tipp mit dem
DBGrid1.Columns.SaveToFile('c:\sortierung.txt');
ist toll, und anschließend funktioniert auch die Anzeige.
Ich möchte duiesen Inhalt aber in eine Ini-Datei ablegen. Hier verzweifel ich. Habe auch keine Ahnung, wie ich einen TStringStream direkt in eine INI-Section bekomme.

mfg waldforest



[delphi]
procedure TQueryForm.GridToIni(GridName: TDBGrid; Section: string);
var
MS: TMemoryStream;
Ini : TIniFile;
tTv: TStringStream;
node : string;
n: integer;
begin
tTv := TStringStream.Create('');
MS := TMemoryStream.Create;
Ini:=TIniFile.Create(ChangeFileExt(Application.Exe Name,'.ini'));
try
GridName.Columns.SaveToStream(MS);
MS.Position := 0;
tTv.Read(MS,ms.Size) ;


INI.EraseSection(Section);
INI.WriteString(Section, '', tTv.DataString); // Hier läuft die Anwendung auf Fehler

finally
tTv.Free;
MS.Free;
end;
end;


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

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