Gedankenspiel nonvisueller Grid
-
- Lazarusforum e. V.
- Beiträge: 2809
- Registriert: Sa 9. Sep 2006, 18:05
- OS, Lazarus, FPC: Linux (L trunk FPC trunk)
- CPU-Target: 64Bit
- Wohnort: Dresden
- Kontaktdaten:
Gedankenspiel nonvisueller Grid
Es ist nur eine Idee und vielleicht ist der Gedanke auch grundlegend falsch...
Ich persönlich arbeite sehr oft (Da es meist datenbankgeschichten sind) mit Grids, genauer gesagt, meistens mit dem TStringGrid. Ich finde das Stringgrid sowohl zur Darstellung als auch zur Verarbeitung der Daten (Lesen, Schreiben, Speichern usw...) ausgesprochen praktisch und einfach.
Nur es gibt ja auch Daten, die als Tabelle vorliegen (und ein Grid ist ja letztlich nichts anderes) aber eben in diesem Moment nicht dargestellt werden sollen.
Nun würde es sich anbieten, ein nonvisuelles Objekt zu haben, welches sich ähnlich verhält, wie ein Stringgrid, also Rows und Colums, Cells usw.
Nur wie sollte man das realisieren? Gewiss könnte man auch direkt zweidimensionale dynamische Arrays nehmen aber dies setzt natürlich ein völlig anderes Ansprechen voraus, als bei nem Stringgrid.
Gibt es eine Möglichkeit, von TCustomGrid eine nichtvisuelle Klasse abzuleiten, quasi alles was die Bildschirmausgabe angeht zu entfernen?
Oder sollte man eine komplett neue Klasse konstruieren, die intern auf nem 2D dyn. Array aufbaut und nach außen an den Stringgrid angelehnt ist?
Oder gäbe es noch eine völlig andere Variante, ein ähnliches Verhalten nachzuahmen?
Ich persönlich arbeite sehr oft (Da es meist datenbankgeschichten sind) mit Grids, genauer gesagt, meistens mit dem TStringGrid. Ich finde das Stringgrid sowohl zur Darstellung als auch zur Verarbeitung der Daten (Lesen, Schreiben, Speichern usw...) ausgesprochen praktisch und einfach.
Nur es gibt ja auch Daten, die als Tabelle vorliegen (und ein Grid ist ja letztlich nichts anderes) aber eben in diesem Moment nicht dargestellt werden sollen.
Nun würde es sich anbieten, ein nonvisuelles Objekt zu haben, welches sich ähnlich verhält, wie ein Stringgrid, also Rows und Colums, Cells usw.
Nur wie sollte man das realisieren? Gewiss könnte man auch direkt zweidimensionale dynamische Arrays nehmen aber dies setzt natürlich ein völlig anderes Ansprechen voraus, als bei nem Stringgrid.
Gibt es eine Möglichkeit, von TCustomGrid eine nichtvisuelle Klasse abzuleiten, quasi alles was die Bildschirmausgabe angeht zu entfernen?
Oder sollte man eine komplett neue Klasse konstruieren, die intern auf nem 2D dyn. Array aufbaut und nach außen an den Stringgrid angelehnt ist?
Oder gäbe es noch eine völlig andere Variante, ein ähnliches Verhalten nachzuahmen?
Johannes
Fand deine Idee ganz lustig (obwohl ich nicht weiss wozu 
Hab mal zum entspannen einen Vorschlag gebastelt.
Ist nicht fertig, das kannste selber machen, aber funzt soweit für eine Demo (kann sein dass es noch Bugs gibt, ist ja kaum getestet)
Du kannst auf die Cells zugreifen, ohne vorher Row/ColCount zu setzen.
Auch Zuweisungen innerhalb des TStrings werden bearbeitet.
Also Sg.Rows[5].Text:=....
Ist nur ein Vorschlag.

Hab mal zum entspannen einen Vorschlag gebastelt.
Ist nicht fertig, das kannste selber machen, aber funzt soweit für eine Demo (kann sein dass es noch Bugs gibt, ist ja kaum getestet)
Du kannst auf die Cells zugreifen, ohne vorher Row/ColCount zu setzen.
Auch Zuweisungen innerhalb des TStrings werden bearbeitet.
Also Sg.Rows[5].Text:=....
Ist nur ein Vorschlag.
Code: Alles auswählen
{ TMyStringGrid }
TMyStringGrid=class
private
fRows:TList;
fCols:TStringList;
fColCount:integer;
fChangeLock:integer;
function GetCells(ACol, ARow: Integer): string;
function GetCols(index: Integer): TStrings;
function GetRows(index: Integer): TStrings;
procedure SetCells(ACol, ARow: Integer; const AValue: string);
procedure SetCols(index: Integer; const AValue: TStrings);
procedure SetRows(index: Integer; const AValue: TStrings);
procedure SetColCount(const AValue: Integer);
procedure SetRowCount(const AValue: Integer);
function GetColCount: Integer;
function GetRowCount: Integer;
procedure DoOnChange(Sender:TObject);
public
constructor Create;
destructor Destroy; override;
property Cells[ACol, ARow: Integer]: string read GetCells write SetCells;
property Cols[index: Integer]: TStrings read GetCols write SetCols;
property Rows[index: Integer]: TStrings read GetRows write SetRows;
property ColCount: Integer read GetColCount write SetColCount;
property RowCount: Integer read GetRowCount write SetRowCount;
end;
implementation
{ TMyStringGrid }
constructor TMyStringGrid.Create;
begin
fChangeLock:=0;
fRows:=TList.Create;
fCols:=TStringList.create;
end;
destructor TMyStringGrid.Destroy;
var i:integer;
begin
fCols.free;
for i:=0 to fRows.count-1 do TStringList(fRows[i]).free;
fRows.free;
inherited Destroy;
end;
function TMyStringGrid.GetCells(ACol, ARow: Integer): string;
begin
inc(fChangeLock);
if (ARow<RowCount) and (ACol<ColCount) then
Result:=GetRows(ARow)[ACol];
dec(fChangeLock);
end;
function TMyStringGrid.GetCols(index: Integer): TStrings;
var i:integer;
begin
fCols.Clear;
for i:=0 to RowCount-1 do
fCols.add(GetRows(i)[index]);
Result:=fCols;
end;
function TMyStringGrid.GetRows(index: Integer): TStrings;
begin
Result:=TStrings(fRows[index]);
end;
procedure TMyStringGrid.SetCells(ACol, ARow: Integer; const AValue: string);
begin
inc(fChangeLock);
if ACol>ColCount-1 then SetColCount(ACol+1);
if ARow>RowCount-1 then SetRowCount(ARow+1);
GetRows(ARow)[ACol]:=AValue;
dec(fChangeLock);
end;
procedure TMyStringGrid.SetCols(index: Integer; const AValue: TStrings);
begin
//Todo
end;
procedure TMyStringGrid.SetRows(index: Integer; const AValue: TStrings);
begin
//Todo
end;
procedure TMyStringGrid.SetColCount(const AValue: Integer);
var i, j:integer;
SL:TStringList;
begin
inc(fChangeLock);
fColCount:=AValue;
for i:=0 to fRows.Count-1 do
begin
SL:=TStringList(fRows[i]);
if SL.Count<AValue then for j:=0 to AValue-SL.Count-1 do SL.Add('');
if SL.Count>AValue then for j:=0 to SL.Count-AValue-1 do SL.Delete(Sl.count-1);
end;
dec(fChangeLock);
end;
procedure TMyStringGrid.SetRowCount(const AValue: Integer);
var i,j, Rc:integer;
TempSL:TStringList;
begin
inc(fChangeLock);
Rc:=fRows.Count;
if AValue>Rc then for i:=0 to AValue-Rc-1 do
begin
TempSL:=TStringList.create;
TempSl.OnChange:=@DoOnChange;
fRows.Add(TempSL);
for j:=0 to fColCount do TempSL.Add('');
end;
if AValue<Rc then for i:=0 to Rc-AValue-1 do
begin
TStringList(fRows[FRows.Count-1]).free;
fRows.Delete(FRows.Count-1);
end;
dec(fChangeLock);
end;
function TMyStringGrid.GetColCount: Integer;
begin
Result:=fColCount;
end;
function TMyStringGrid.GetRowCount: Integer;
begin
Result:=fRows.Count;
end;
procedure TMyStringGrid.DoOnChange(Sender: TObject);
var TempSL:TStringList;
begin
if fChangeLock=0 then
begin
TempSL:=TStringList(Sender);
if TempSL.Count>ColCount then SetColCount(TempSl.Count);
//writeln('change');
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var Sg:TMyStringGrid;
var i,j:integer;
begin
Sg:=TMyStringGrid.Create;
Sg.Cells[0,0]:='A0';
Sg.Cells[1,1]:='B1';
Sg.Cells[2,2]:='C2';
Sg.Cells[3,3]:='D3';
Sg.Cells[5,4]:='E5';
Sg.Cells[0,5]:='A5';
Sg.Cells[2,0]:='C0';
Sg.Rows[5].Text:='F0'+LineEnding+'F1'+LineEnding+'F3'+LineEnding+'F4'+LineEnding+'F5'+LineEnding+'F6'+LineEnding+'F7';
write('Rows: ',Sg.RowCount);
writeln(' Cols: ',Sg.ColCount);
writeln('--grid-');
for i:=0 to Sg.RowCount-1 do
begin
for j:=0 to Sg.ColCount-1 do write(Sg.Cells[j,i]+' ');
writeln('');
end;
writeln('--cols-');
writeln(Sg.Cols[0].text);
writeln('--rows-');
writeln(Sg.Rows[0].text);
Sg.free;
end;
- af0815
- Lazarusforum e. V.
- Beiträge: 6857
- Registriert: So 7. Jan 2007, 10:20
- OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
- CPU-Target: 32Bit (64Bit)
- Wohnort: Burgenland
- Kontaktdaten:
Nicht auch möglich/sinnvoll vom 'TVirtualGrid' aus der Grids weg zu arbeiten ?!
Bezüglich Sinnhaftigkeit - jeder kennt das StringGrid - ist eine prima Komponente, nur mit viele Overhead, wenn man die nicht visuell benutzt. Deshalb finde ich, kann eine nonvisual schon deutliche Vorteile haben.
Mein Vorschlag wäre, sie so kompatibel wie möglich zu machen, nur halt ohne den sichtbaren Teil. Vor allen, welche Methoden und Eigenschaften werden benötigt ?!
Bezüglich Sinnhaftigkeit - jeder kennt das StringGrid - ist eine prima Komponente, nur mit viele Overhead, wenn man die nicht visuell benutzt. Deshalb finde ich, kann eine nonvisual schon deutliche Vorteile haben.
Mein Vorschlag wäre, sie so kompatibel wie möglich zu machen, nur halt ohne den sichtbaren Teil. Vor allen, welche Methoden und Eigenschaften werden benötigt ?!
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).
- af0815
- Lazarusforum e. V.
- Beiträge: 6857
- Registriert: So 7. Jan 2007, 10:20
- OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
- CPU-Target: 32Bit (64Bit)
- Wohnort: Burgenland
- Kontaktdaten:
als dynamischer 2D Behälter mit dem Komfort vom Stringgrid, schon klar, alternativen sind das StringGrid (lol) und das TMemDataset. Oder gibts noch weitere 2D Datenbehälter ?
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).
-
- Lazarusforum e. V.
- Beiträge: 2809
- Registriert: Sa 9. Sep 2006, 18:05
- OS, Lazarus, FPC: Linux (L trunk FPC trunk)
- CPU-Target: 64Bit
- Wohnort: Dresden
- Kontaktdaten:
So war das nicht gemeind, denn, wie af schon schreibt, das beseitigt ja nicht den Overhead den visuellen Sachen dennoch mitbringen.shokwave hat geschrieben:öhm, vielleicht lieg ich auch falsch, aber Visible:=False und gut?
Af hat genau mein Anliegen beschrieben, ein 2D-Container mit Stringgridkomfort, der optimaler Weise direkt ein Stringgrid ersetzen kann, ohne das die Bezeichner usw. umgeschrieben werden müssen...folglich sollten alle Properties, die relevant sind, die gleichen Bezeichner tragen.
Ob man nun alle Eigenschaften übernimmt, sei mal dahingestellt.
Muss ich mir mal anschauen.af hat geschrieben:'TVirtualGrid'
Ansonsten geht theos Vorschlag ja schon schön in die Richtung and lässt sich ausbauen.
? Genau das solls doch werden, ne Kalsse die sofort mit nem Stringgrid getauscht werden könnte, ohne erst große Veränderungen an den Zugreifenden Funktionen durchführen zu müssen.Christian hat geschrieben:Ne Spezifisch angepasste Klasse...
Johannes
-
- Lazarusforum e. V.
- Beiträge: 2809
- Registriert: Sa 9. Sep 2006, 18:05
- OS, Lazarus, FPC: Linux (L trunk FPC trunk)
- CPU-Target: 64Bit
- Wohnort: Dresden
- Kontaktdaten:
Ich bastel mir gerade ne Klasse zusammen, aber wie kann ich erreichen, das auf das setzen einer TStrings-Eigenschaft reagiert wird? Denn wie soll ich darauf reagieren, wenn die entsprechende Prozedure niemals aufgerufen wird?
Code: Alles auswählen
TNoViGrid=class
private
Grid: Array of Array of string;
fRows:TStringList;
fCols:TStringList;
procedure SetRows(index: Integer; const AValue: TStrings);
function GetRows(index: Integer): TStrings;
public
property Rows[index: Integer]: TStrings read GetRows write SetRows;
end;
//...
procedure TNoViGrid.SetRows(index: Integer; const AValue: TStrings);
var i: integer;
begin
ShowMessage('Test');
//^^wird bei Zuweisung an Rows[] nicht ausgeführt
end;
Johannes
monta hat geschrieben:Ich bastel mir gerade ne Klasse zusammen, aber wie kann ich erreichen, das auf das setzen einer TStrings-Eigenschaft reagiert wird? Denn wie soll ich darauf reagieren, wenn die entsprechende Prozedure niemals aufgerufen wird?
Klappt bei mir problemlos;
Grid.Rows[0]:=Memo1.lines;
Wieso machste jetzt alles nochmal neu?
Was ist denn an meinem Vorschlag oben nicht gut?
-
- Lazarusforum e. V.
- Beiträge: 2809
- Registriert: Sa 9. Sep 2006, 18:05
- OS, Lazarus, FPC: Linux (L trunk FPC trunk)
- CPU-Target: 64Bit
- Wohnort: Dresden
- Kontaktdaten:
Der vorschlag ist gut, ich wolltee s nur nochmal mit dem dyn. Array probieren.
Aber irgendwie hat ich eh nen Denkfehler, dein Code funktioniert bestens, wie ich gerade festgestellt hab. Keine Ahnung, was ich die Nacht falsch gemacht hab. Ich hab mich davon täuschen lassen, das SetRows und SetColums bei dir nicht belegt ist. der Irrtum lag am Denkfehler kein Set... also wird die Eigenschaft nicht übernohmen.
Aber wann wird nun eigentlich wirklich SetCols/SetRows aufgerufen?
(Ich nehm mal an, ich darf den Code so auch übernehmen?)
Aber irgendwie hat ich eh nen Denkfehler, dein Code funktioniert bestens, wie ich gerade festgestellt hab. Keine Ahnung, was ich die Nacht falsch gemacht hab. Ich hab mich davon täuschen lassen, das SetRows und SetColums bei dir nicht belegt ist. der Irrtum lag am Denkfehler kein Set... also wird die Eigenschaft nicht übernohmen.
Aber wann wird nun eigentlich wirklich SetCols/SetRows aufgerufen?
(Ich nehm mal an, ich darf den Code so auch übernehmen?)
Johannes
Wie oben gesagt: z.B. Grid.Rows[0]:=Memo1.lines; ruft SetRows auf, oder nicht?monta hat geschrieben: Aber wann wird nun eigentlich wirklich SetCols/SetRows aufgerufen?
Kannst du mal ein Beispiel machen, wo man dein Problem damit sehen kann?
Wenn du den machst: Grid.Rows[0][3]:='AHA'; wird natürlich ein GetRows gemacht, kein SetRows!.
Von dieser Änderung kriegt du über DoOnChange Wind.
Besser wäre natürlich hier das Cells Property zu verwenden.
Du musst dich erst bei mir als Code-User registrieren.monta hat geschrieben: (Ich nehm mal an, ich darf den Code so auch übernehmen?)
(Mädchennamen BEIDER Grossmütter nicht vergessen!)
Danach kannst du den Code aktivieren lassen fur 5 Benutzungen.
Bitte HD und Netzwerkarten-ID sowie Carosserienummer deines Computers angeben.
Wenn du den Code auf einem anderen Rechner ausführen wilst, bitte mich telefonisch um eine Erhöhung des Benutzungs-limits anflehen.
Achso, bin ja weder MS noch Codegear.
Dann.... Ja kannste übernehmen.

-
- Lazarusforum e. V.
- Beiträge: 2809
- Registriert: Sa 9. Sep 2006, 18:05
- OS, Lazarus, FPC: Linux (L trunk FPC trunk)
- CPU-Target: 64Bit
- Wohnort: Dresden
- Kontaktdaten:
Nein, oder wir reden aneinander vorbei.Wie oben gesagt: z.B. Grid.Rows[0]:=Memo1.lines; ruft SetRows auf, oder nicht?
Code: Alles auswählen
procedure TNVStringGrid.SetRows(index: Integer; const AValue: TStrings);
begin
//Todo
WriteLn('Test');
end;
Code: Alles auswählen
Grid.Rows[i].DelimitedText := Liste.Strings[i];
Und es gibt noch ein seltsames Phänomen:
Code: Alles auswählen
procedure TForm1.Button1Click (Sender: TObject );
var Grid: TNVStringGrid;
Liste: TStringList;
i: integer;
Count: integer;
begin
//...
Liste.LoadFromFile('xyz');
Grid.ColCount := 4;
//>>
count := Liste.Count;
ShowMessage(inttostr(Count));
Grid.RowCount := Count;// 362;
ShowMessage(inttostr(Count));
for i := 0 to {361 do } Count - 1 do;
//<<
begin
Grid.Rows[i].Delimiter := ';';
Grid.Rows[i].QuoteChar := '"';
Grid.Rows[i].DelimitedText := Liste.Strings[i];
end;
Grid.WriteContentToShell;
//...
end;
Ersetze ich Count durch den konkreten Wert 362 werden alle Zeilen korrekt in den Grid übernohmen und in der Konsole ausgegeben.
TNVStringGrid ist exakt Theos Klasse, mit der Ergänzung von WriteContentToShell.
Johannes
Ich hab dich schon verstanden. SetRows wird nur so aufgerufen: Grid.Rows[0]:=Memo1.lines;monta hat geschrieben:
Nein, oder wir reden aneinander vorbei.
...
Bspw. bei folgendem Code müsste doch SetRows aufgerufen werden (oder hab ich da einen Grundlegenden Verständnisfehler?):Code: Alles auswählen
Grid.Rows[i].DelimitedText := Liste.Strings[i];
In deinem Code HOLST du Grid.Rows (also eigentlich den Pointer auf TStringList) und machst was damit. Das ist kein SetRows sondern ein GetRows!
Eigentlich genau wie:
TempSL:=Grid.Rows;
TempSL.DelimitedText:=....
und nicht
Grid.Rows:=....
Wie ich schon geschrieben habe, hänge ich deswegen auch:
Code: Alles auswählen
TempSl.OnChange:=@DoOnChange;