Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   EListError, warum? ObjectList mit Daten füllen (https://www.delphipraxis.net/210634-elisterror-warum-objectlist-mit-daten-fuellen.html)

delphifan2004 20. Mai 2022 14:46


EListError, warum? ObjectList mit Daten füllen
 
Wie kann ich Daten in eine Stringliste korrekt übernehmen?

Ich will die Grid Komponente mal selber nachbauen.

Ich habe folgenden Code:

Delphi-Quellcode:
procedure TCustomGrid.Paint;
var ACol,ARow,w,h,l,t,r,b: Integer;
begin
  inherited Paint;
  for ARow:=0 to FRowsContainer.Count-1 do
  for ACol:=0 to TStringList(FRowsContainer.Items[ARow]).Count-1 do
  begin
    w := TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width;
    h := TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Height;
    l := TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Left;
    t := TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Top;

    DrawRectangleClip(vscreen,l,t,l+w,t+h,ToColor(colBlack));

    t := t + h;
    l := l + w;
  end;
end;
Der Create Konstruktor sieht so aus:

Delphi-Quellcode:
constructor TCustomGrid.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FRowsContainer := TObjectList.Create;
  FRowsContainer.Capacity := 10;
  FColsContainer := TStringList.Create; //wird gar nicht benötigt, kann aber zum Speichern zusätzlicher Infos verwendet werden
  FColsContainer.Capacity := 5;
  ClipRect := Rect(0,0,600,36);
  FHeight := ClipRect.Bottom - ClipRect.Top;
  FWidth := ClipRect.Right - ClipRect.Left;
  FRowHeight := 26;
  FColWidth := 60;
  CreateRows(9);
  CreateCols(5);
  FFixedCols := 0;
  FFixedRows := 0;
end;

procedure TCustomGrid.CreateCols(Count: Integer);
begin
  SetCols(Count);
end;

procedure TCustomGrid.CreateRows(Count: Integer);
begin
  SetRows(Count);
end;

procedure TCustomGrid.SetRows(AValue: Integer); //Zuerst SetRows, danach SetCols
var ARow: Integer; AList: TStringList;
begin
  if FRows=AValue then Exit;
  FRows:=AValue;
  for ARow := 0 to FRows-1 do
  begin
    AList := TStringList.Create; AList.Capacity := 5;
    FRowsContainer.Add(AList);
  end;
end;

procedure TCustomGrid.SetCols(AValue: Integer); //Passiert am Anfang vor Benutzung des Grids
var ACol,ARow: Integer;
begin
  if FCols=AValue then Exit;
  FCols:=AValue;
  for ARow := 0 to FRowsContainer.Count -1 do
  begin
    for ACol := 0 to FCols-1 do TStringList(FRowsContainer.Items[ARow]).AddObject('        ', TGridColumn.Create(self));
    TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width := FColWidth;
    TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Height := FRowHeight;
  end;
end;
Die relevanten Datenstrukturen sind wie folgt, definiert:

Das Grid enhält in einer Stringliste die Spaltenstrings, in Objects eine TGridColumn mit allen Daten. Eine TObjectList nimmt je Zeile dann eine TStringList auf, deren Strings die
Spalten bilden, sofern die Spalteninhalte Strings sind oder bei Integer oder Double Werten die String Repräsentation dieser Werte.

Delphi-Quellcode:
  { TGridColDimension }

  TGridColDimension = record
    Height: Integer;
    Width: Integer;
  end;

  TGridDataItem = record
    Dimension: TGridColDimension;
    Field: TObject;                    //Für ein TField Objekt
    //Fieldnum: Integer;
    case Longword of
     0: (BinData : TObject);           //Binärdaten aufnehmen
     1: (Contents: Longword);
     2: (Int32Val: Integer);
     3: (FloatVal: Double);
     4: (Int16Val: smallint);
  end;

  { TGridGenericItem }

  TGridGenericItem = class(TObject)
  private
    FGridData: TGridDataItem;
    function GetGridData: TGridDataItem;
    procedure SetGridData(AValue: TGridDataItem);
  public

    constructor Create;
    constructor Create(AData: TObject);
    destructor Destroy; override;
    property GridData: TGridDataItem read GetGridData write SetGridData;

  end;

  { TGridColumn }

  TGridColumn = class(TBaseControl)
  private
    FItem: TGridGenericItem;
    FLeft: Integer;
    FTop: Integer;
    function GetContent: TGridGenericItem;
    function GetWidth: Integer;
    function GetHeight: Integer;
    procedure SetContent(AValue: TGridGenericItem);
    procedure SetLeft(AValue: Integer);
    procedure SetTop(AValue: Integer);
    procedure SetWidth(AValue: Integer);
    procedure SetHeight(AValue: Integer);
  public
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
    procedure Paint; override;
    property Content: TGridGenericItem read GetContent write SetContent;
    property Left: Integer read FLeft write SetLeft;
    property Top: Integer read FTop write SetTop;
    property Width: Integer read GetWidth write SetWidth;
    property Height: Integer read GetHeight write SetHeight;
  end;

  { TCustomGrid }

  TCustomGrid = class(TBaseControl)
  private
    FCols: Integer;
    FFixedCols: Integer;
    FFixedRows: Integer;
    FRows: Integer;
    FColsContainer: TStrings; //in Objects[Index] -> TGridGenericItem
    FRowsContainer: TObjectList;
    FRowHeight: Integer;
    FColWidth: Integer;
    function GetCells(X, Y: Integer): String;
    function GetColHeights(Index: Integer): Integer;
    function GetColWidths(Index: Integer): Integer;
    function GetItems(X,Y: Integer): TGridColumn;
    procedure SetCells(X, Y: Integer; AValue: String);
    procedure SetColHeights(Index: Integer; AValue: Integer);
    procedure SetCols(AValue: Integer);
    procedure SetColWidth(AValue: Integer);
    procedure SetColWidths(Index: Integer; AValue: Integer);
    procedure SetFixedCols(AValue: Integer);
    procedure SetFixedRows(AValue: Integer);
    procedure SetItems(X,Y: Integer; AValue: TGridColumn);
    procedure SetRowHeight(AValue: Integer);
    procedure SetRows(AValue: Integer);
  public
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
    procedure AddRow;
    procedure CreateCols(Count: Integer);
    procedure CreateRows(Count: Integer);
    procedure Paint; override;
    property Cells[X,Y: Integer]: String read GetCells write SetCells;
    property Cols: Integer read FCols write SetCols; //Anzahl Spalten. Count of columns.
    property Rows: Integer read FRows write SetRows; //Anzahl Zeilen. Count of rows.
    property FixedCols: Integer read FFixedCols write SetFixedCols; //for column title
    property FixedRows: Integer read FFixedRows write SetFixedRows; //for row title
    property Items[X,Y: Integer]: TGridColumn read GetItems write SetItems; //Inhalte der Zellen, in cells immer als String, hier aber die realen Werte
    property RowHeight: Integer read FRowHeight write SetRowHeight;
    property ColWidth: Integer read FColWidth write SetColWidth;
    property ColHeights[Index: Integer]: Integer read GetColHeights write SetColHeights; //Rückgabetyp noch überlegen
    property ColWidths[Index: Integer]: Integer read GetColWidths write SetColWidths;
  end;

Was mache ich mit der Stringliste falsch?


Ich bekommen einen EListError, der mir als Meldung die letzte Zeile der Objektliste FRowsContainer anzeigt. Warum. Wie kann ich dieses Problem elegant umgehen?

venice2 20. Mai 2022 15:21

AW: EListError, warum? ObjectList mit Daten füllen
 
Habe jetzt nicht nach deinen fehler gesucht aber könnte man das nicht auch so schreiben?
Delphi-Quellcode:
    t := t + h;
    l := l + w;
Delphi-Quellcode:
    inc(t, h);
    inc(l, w);

Uwe Raabe 20. Mai 2022 15:41

AW: EListError, warum? ObjectList mit Daten füllen
 
Warum überhaupt dieses Hochzählen, wo die Werte doch im nächsten Durchlauf wieder gesetzt werden?

venice2 20. Mai 2022 16:07

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1506117)
Warum überhaupt dieses Hochzählen, wo die Werte doch im nächsten Durchlauf wieder gesetzt werden?

dein Einwand ist berechtigt.

EDIT:
Eventuell so wenn t und l global definiert sind.

Delphi-Quellcode:
    l := l + TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Left;
    t := t + TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Top;

    inc(t, h);
    inc(l, w);

Incocnito 20. Mai 2022 16:19

AW: EListError, warum? ObjectList mit Daten füllen
 
Moin,

vielleicht solltest du ein kleines Test-Programm posten, dann kann man das auch leichter testen.

Was mir komisch vorkommt, so beim ersten drüber schauen (neben dem von Uwe Raabe), dass
du in den Methoden SetRows und SetCols die Liste von 0 bis zur gewünschten neuen Zeilenzahl (um mal bei SetRows zu bleiben)
füllst. Wird das aber ein zweites mal aufgerufen leerst du die derzeitige Liste nicht,
bzw. füllst nur noch den neuen Anteil auf.

Als zweites würde ich in der geschachtelten Forschleife TStringList(FRowsContainer.Items[ARow])
zwischenspeichern ... das macht das Ganze etwas lesbarer meiner Meinung nach.
Außerdem kannst du dann auch leicht in Delphi beim Debuggen in die StingrListe rein schauen,
was auch hilfreich sein kann.

Als drittes fehlt in SetCols in der inneren For-Schleife das begin-end, wodurch nur
die eine Zeile ausgeführt wird.
Auch hier wieder mein Appell: Macht auch für einzeilige Sachen bei bedingungen (if) oder schleifen (for, while, ...)
IMMER begin-end. Du bist nicht der erste der solch einen Fehler hat.
Das klang jetzt negativer als es gemeint war. Bitte nicht negativ lesen! 😅

Ich hoffe das hilft etwas.

Liebe Grüße
Incocnito

delphifan2004 20. Mai 2022 16:23

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1506117)
Warum überhaupt dieses Hochzählen, wo die Werte doch im nächsten Durchlauf wieder gesetzt werden?

Weil t=top ist und irgendwie die Höhe der Zeile her muss. Aber ok, dennoch danke für den Hinweis, ich guck mir den Code noch mal an, vielleicht geht das eleganter zu lösen. Ich brauche auf jeden Fall das aktuelle Top und die Höhe der Zeile.

Aber warum bekomme ich den EListError? Und wie kann ich den bei solchen Listen ein für allemal umgehen, den hatte ich schon mehrfach mit Listen?

venice2 20. Mai 2022 16:25

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

Weil t=top ist und irgendwie die Höhe der Zeile her muss.
Siehe mein Edit.

Uwe Raabe 20. Mai 2022 16:30

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

Zitat von delphifan2004 (Beitrag 1506124)
Aber warum bekomme ich den EListError?

Könnte es sein, dass hier die beiden letzten Zeilen noch in die For-Schleife gehören und da lediglich ein Begin/End fehlt? (Der Compiler sollte eine Warnung liefern!)
Delphi-Quellcode:
    for ACol := 0 to FCols-1 do TStringList(FRowsContainer.Items[ARow]).AddObject(' ', TGridColumn.Create(self));
    TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width := FColWidth;
    TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Height := FRowHeight;
Besser wäre aber das ganze wie oben schon erwähnt mit einer lokalen Variable zu lösen:
Delphi-Quellcode:
    for ACol := 0 to FCols-1 do begin
      var gridColumn := TGridColumn.Create(self);
      gridColumn.Width := FColWidth;
      gridColumn.Height := FRowHeight;
      TStringList(FRowsContainer.Items[ARow]).AddObject(' ', gridColumn);
    end;

delphifan2004 20. Mai 2022 17:57

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1506127)
Zitat:

Zitat von delphifan2004 (Beitrag 1506124)
Aber warum bekomme ich den EListError?

Könnte es sein, dass hier die beiden letzten Zeilen noch in die For-Schleife gehören und da lediglich ein Begin/End fehlt? (Der Compiler sollte eine Warnung liefern!)
Delphi-Quellcode:
    for ACol := 0 to FCols-1 do TStringList(FRowsContainer.Items[ARow]).AddObject(' ', TGridColumn.Create(self));
    TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width := FColWidth;
    TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Height := FRowHeight;
Besser wäre aber das ganze wie oben schon erwähnt mit einer lokalen Variable zu lösen:
Delphi-Quellcode:
    for ACol := 0 to FCols-1 do begin
      var gridColumn := TGridColumn.Create(self);
      gridColumn.Width := FColWidth;
      gridColumn.Height := FRowHeight;
      TStringList(FRowsContainer.Items[ARow]).AddObject(' ', gridColumn);
    end;

OK, danke, ist geändert, allerdings habe ich den EListError immer noch. Irgendwas Anderes ist da noch verkehrt. Ich sehe aber nicht, was, vielleicht sollte ich für heute aufhören und Abstand gewinnen und übers WE noch mal schauen.

Delphi.Narium 20. Mai 2022 19:07

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

Zitat von delphifan2004
Ich bekommen einen EListError, der mir als Meldung die letzte Zeile der Objektliste FRowsContainer anzeigt.

Und welche Zeile genau ist das?

Die letzte im Eingangspost oder die letzte in welcher der Prozeduren?

Zitat:

EListError is the exception class for list and string errors.

EListError is raised when an error is made in a list, TStrings, or TStringList object. This exception commonly occurs when an application refers to an item that is out of the list's range.

EListError also occurs if an application tries to add a duplicate string to a TStringList object when the value of the Duplicates property is dupError.

An EListError exception is raised when an application attempts to insert a string into a sorted string list, since the insertion of a string at a specified position may put the list out of order.
Welche Variante des Fehler EListError kommt denn hier in Frage?

Ein Fehler, der durch nicht ganz korrekte Zählweise entstehen könnte oder die Variante, dass da irgendwo eine Dublette in 'ner Stringliste eingefügt wird?

Bei Zählfehlern könnte man ggfls. hier im Forum behilflich sein, wenn die exakte Fehlerzeile mal bekanntgegeben wird. Einen potentiellen Dublettenfehler können wir so ohne weiteres nicht erkennen. Weder kennen wir die zu verarbeitenden Daten noch wissen wir, ob die Stringlisten sortiert sein könnten und das Einfügen von Dubletten eventuell nicht möglich ist.

Die dritte Variante könnte eintreten, wenn bei einer Liste, deren
Delphi-Quellcode:
AList.Capacity := 5;
ist, das sechste Element eingefügt wird.
Hier könnte ggfls. ein
Delphi-Quellcode:
SetCols(6);
(oder ein beliebiger anderer Wert > 5?) Fehlerursache sein.

Oder ich könnte mit meinen Mutmaßungen auch vollständig daneben liegen ;-)

delphifan2004 21. Mai 2022 12:53

AW: EListError, warum? ObjectList mit Daten füllen
 
Huch! Doublette!

Wie macht delphi das in seinem Stringgrid?

Ich codiere:

Delphi-Quellcode:
TStringList(FRowsContainer.Items[ARow]).AddObject(' ', gridColumn);
Und da ist sie, die Doublette! Trotz AList.Duplicates := dupAccept in SetRows! tritt jetzt der Feler immr noch auf, allerdings verändert, das Programm hängt fest, der EListError erscheint nun nicht mehr, aber das Programm hängt.

Wie macht das Delphi mit der Stringliste die auch dort die Strings aufnimmt?

Der Fehler trat zuerst hier auf:
Delphi-Quellcode:
 if (ACol<TStringList(FRowsContainer.Items[ARow]).Capacity) and Assigned(TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol])) then
    begin
    w := gc.Width; //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width; //#### Hier der EListError ####[]
Hier noch mal die ganze Methode:
Delphi-Quellcode:
procedure TCustomGrid.Paint;
var ACol,ARow,w,h,l,t,r,b: Integer; gc: TGridColumn; sl: TStringList; rc: TObjectList;
begin
  inherited Paint;
  for ARow:=0 to FRowsContainer.Count-1 do
  for ACol:=0 to TStringList(FRowsContainer.Items[ARow]).Count-1 do
  begin
    rc := FRowsContainer;
    sl := TStringList(rc.Items[ARow]);
    gc := TGridColumn(sl.Objects[ACol]);
    if (ACol<TStringList(FRowsContainer.Items[ARow]).Capacity) and Assigned(TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol])) then
    begin
    w := gc.Width; //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width;                                                       //#### Hier der EListError ####
    h := gc.Height; //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Height;
    l := gc.Left;  //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Left;
    t := gc.Top;   //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Top;
    end;
    DrawRectangleClip(vscreen,l,t,l+w,t+h,ToColor(colBlack));

    t := t + h;
    l := l + w;
  end;
end;
Siehe auch Eingangsbeitrag, dortiger Code.


In SetRows -> AList.Capacity:=5 muss ich natürlich noch anpassen auf beliebig viele Elemente. Aktuell aber sollen für den Test 5 Spalten genügen.

Die Schwierigkeit ist hier, dass ich zunächst eine Zeile erzeigen will, aber noch nicht weiß, wie viele Spalten die haben soll, das entscheidet sich danach erst in SetCols().
Ich brauchte hier eine dynamische Anpassung der Spalten nachdem die Zeile erzeugt ist. Auch hier die Frage, wie macht Delphi das in seinem Stringgrid? Da guck ich inzwischen mal in den Code rein. Vielleicht finde ich ja dort die Lösung des Problems. Vorausgesetzt Emba programmiert nicht zu trickreich. :)

delphifan2004 21. Mai 2022 17:06

AW: EListError, warum? ObjectList mit Daten füllen
 
Debuggen ergibt in SetRows die Fortsetzung

for ACol := 0 to FCols-1 do

eine Zählung über Spalte 9 hinaus. Warum das? Ich will 9 Spalten haben und nicht 10 SetCols(9)! 0..9 sind doch schon 10 Spalten, FCols -1 sind 9 Spalten!

Offenbar wird die Abbruchbedingung nicht eingehalten. Wie kann das sein? Die For Schleife sollte doch automatisch weiter zählen und bei Erreichen des Maximalwertes abbrechen.

Ich habe in SetCols() die Zeile

if ACol >= FCols-1 then break ergänzt.

venice2 21. Mai 2022 17:28

AW: EListError, warum? ObjectList mit Daten füllen
 
Ich verweise nochmals auf diese Bitte.. Mit den Fehlern wie sie aktuell sind.
So können wir leider nur raten.
Zitat:

vielleicht solltest du ein kleines Test-Programm posten, dann kann man das auch leichter testen.

delphifan2004 21. Mai 2022 18:02

AW: EListError, warum? ObjectList mit Daten füllen
 
Da versuch ich mal ein Testprogramm zu bauen, das aktuelle verwendet noch andere Units, die machen die Suche nur unübersichtlich. Ich baue ein neues Programm nur mit dem Grid.

Wo ist dieses Edit, das ich mir anschauen soll?

venice2 21. Mai 2022 18:49

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

Zitat von delphifan2004 (Beitrag 1506161)
Da versuch ich mal ein Testprogramm zu bauen, das aktuelle verwendet noch andere Units, die machen die Suche nur unübersichtlich. Ich baue ein neues Programm nur mit dem Grid.

Wo ist dieses Edit, das ich mir anschauen soll?

Hier!

So wie es vorher(aktuell ist) kann es nicht funktionieren.
Delphi-Quellcode:
   
  l := TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Left; // left wird l zugewiesen
  t := TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Top; // Top wird t zugewiesen

  DrawRectangleClip(vscreen,l,t,l+w,t+h,ToColor(colBlack));
 
  // Beide variablen werden nicht verwendet daher gehen diese Zuweisungen auf dieser Basis ins leere.
  t := t + h; // t wird anschließend incrementiert aber nicht mehr benutzt weil die Variable t nicht global definiert ist und vorher nicht zugewiesen wird.
  l := l + w; // l wird anschließend incrementiert aber nicht mehr benutzt weil die Variable l nicht global definiert ist und vorher nicht zugewiesen wird.
Du hättest es spätestens bemerkt wenn du über deinen Fehler hinweg gekommen wärst.

delphifan2004 21. Mai 2022 20:21

AW: EListError, warum? ObjectList mit Daten füllen
 
In diesem Testprojekt ist der EListerror weg aber von Paint wird nichts ausgegeben nur die Zeile "Zurück mit << ENTER > >> ...'

Delphi-Quellcode:
program Project3;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,System.Classes,Contnrs;

type
  TGridColumn = class(Tobject)
  private
    FWidth: Integer;
    FHeight: Integer;

  public
    constructor Create;
    property Width: Integer read FWidth write FWidth;
    property Height: Integer read FHeight write FHeight;
  end;

  TCustomGrid = class(TObject)
  private
    FCols: Integer;
    FRows: Integer;
    FRowsContainer: TObjectList;

    procedure SetCols(AValue: Integer);
    procedure SetRows(AValue: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Paint;
    property Cols: Integer read FCols write SetCols;
    property Rows: Integer read FRows write SetRows;
  end;

{ TGridColumn }

constructor TGridColumn.Create;
begin
  inherited Create;
end;

{ TCustomGrid }

constructor TCustomGrid.Create;
begin
  inherited Create;
  FRowsContainer := TObjectList.Create;
end;

destructor TCustomGrid.Destroy;
begin
  FRowsContainer.Free;
  inherited;
end;

procedure TCustomGrid.SetCols(AValue: Integer);
var ACol,ARow: Integer; gridColumn: TGridColumn;
begin
  if FCols=AValue then Exit;
  FCols:=AValue;

  for ARow := 0 to FRows -1 do
  begin
    for ACol := 0 to FCols-1 do
    begin
      gridColumn := TGridColumn.Create;
      gridColumn.Width := 200;
      gridColumn.Height := 20;
      TStringList(FRowsContainer.Items[ARow]).AddObject(' ', gridColumn);
      if ACol >= FCols-1 then break;
    end;
  end;
end;

procedure TCustomGrid.SetRows(AValue: Integer);
var ARow: Integer; AList: TStringList;
begin
  if FRows=AValue then Exit;
  FRows:=AValue;
  for ARow := 0 to FRows-1 do
  begin
    AList := TStringList.Create; AList.Capacity := 5;
    AList.Duplicates:=dupAccept;
    FRowsContainer.Add(AList);
  end;
end;

procedure TCustomGrid.Paint;
var ACol,ARow,w,h,l,t,r,b: Integer; gc: TGridColumn; sl: TStringList; rc: TObjectList;
begin
  for ARow:=0 to FRowsContainer.Count-1 do
  begin
    for ACol:=0 to TStringList(FRowsContainer.Items[ARow]).Count-1 do
    begin
      rc := FRowsContainer;
      sl := TStringList(rc.Items[ARow]);
      gc := TGridColumn(sl.Objects[ACol]);
      if (ACol<TStringList(FRowsContainer.Items[ARow]).Capacity) and Assigned(gc) then
      begin
        w := gc.Width; //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width;
        h := gc.Height; //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Height;
       // l := gc.Left;  //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Left; //aus Originalcode übernommen
       // t := gc.Top;   //TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Top; //aber hier weggelassen
      end;
     // DrawRectangleClip(vscreen,l,t,l+w,t+h,ToColor(colBlack));
     writeln('Breite: ',w);   //wird nicht angezeigt, warum? 
     writeln('Höhe : ',h);   //wird nicht angezeigt, warum? 
     //writeln('Links : ',l);
     //writeln('Oben : ',t);
      {
      t := t + h; //t (Top  soll um h (Zeilenhöhe) nach unten, nachdem Gridrahmenlinie oben gezeichnet ist
      l := l + w; //l (Left) soll um w (Spaltenbreite) nach rechts, nachdem Gridrahmenlinie links gezeichnet ist
      }
      inc(t, h);
      inc(l, w);
    end;
    if ARow >= FRows then break;
  end;
end;


var
  Grid: TCustomGrid;

begin
  try
    { TODO -oUser -cConsole Main : Code hier einfügen }
    Grid:=TCustomGrid.Create;
    Grid.Cols := 9;
    Grid.Rows := 5;
    Grid.Paint;
    writeln('Zurück mit << ENTER >> > >>> ... ');
    readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
@venice2:

Mein Code:

Delphi-Quellcode:
      DrawRectangleClip(vscreen,l,t,l+w,t+h,ToColor(colBlack));
      {
      t := t + h;
      l := l + w;
      }
      inc(t, h);
      inc(l, w);
    end;
    if ARow >= FRows then break;
    //Diese Zeile habe ich hinzugefügt, damit nach Zeile 5 keine weitere hinzugefügt wird. Ich weiß zwar, dass die FOR Schleife automatisch abbricht, ja abbrechen sollte,
    //wenn der Endwert erreicht ist, aber ich habe nun mal den EListError. Übrigens mit Freepascal geschrieben, der Code, aber auch dort ist mir kein Fehler im Compiler bekannt, deshalb habe ich das
    //bisher nicht erwähnt.
Damit werden doch die Variablen l,t,w und h in der DrawRectangleClip Prozedur verwendet. Aber der EListError verhindert, dass ich dorthin komme.
l=Left und t=Top werden zwar mit erhöht, aber da sollte ich ddoch wenigstens die erste Gridspalte sehen, so wie der Code hier aussieht.

Zitat:

Zitat von venice2
// t wird anschließend incrementiert aber nicht mehr benutzt weil die Variable t nicht global definiert ist und vorher nicht zugewiesen wird.

Warum müssen diese vars global definiert sein. Ich will doch lokal zeichnen? Und nach meinem Verständnis ist doch DrawRectangleClip() innerhalb der Schleife. Einen Strich oder Punkt müsste ich doch dann wenigstens sehen? Leider ist mir hier gar nichts klar.


Der von mir gesuchte Fehler muss hier liegen:

Delphi-Quellcode:
      rc := FRowsContainer;
      sl := TStringList(rc.Items[ARow]);   //das hier nicht Assigned oder
      gc := TGridColumn(sl.Objects[ACol]); //das hier nicht Assigned
      if (ACol<TStringList(FRowsContainer.Items[ARow]).Capacity) and Assigned(gc) then
Wenn es das ist, wie kann ich solche Zuweisung zuverlässiger erreichen. Schließlich werden auch in meinem Testprojekt Breite und Höhe nicht angezeigt.

venice2 21. Mai 2022 20:45

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

In diesem Testprojekt ist der EListerror weg aber von Paint wird nichts ausgegeben nur die Zeile "Zurück mit << ENTER > >> ...'
logisch denn TStringList(FRowsContainer.Items[ARow]).Count-1 = -1
Springt gar nicht erst in deine schleife rein.

Prüfe das nochmal..
Delphi-Quellcode:
for ACol:=0 to TStringList(FRowsContainer.Items[ARow]).Count-1 do


Count ist -1 sorry das kann so nichts werden.

venice2 21. Mai 2022 22:19

AW: EListError, warum? ObjectList mit Daten füllen
 
Ok du hast Rows und Cols vertauscht.. deshalb wurde deine TStringliste nicht gefüllt in deinem Beispiel!
Versuche das hier. Habe keinen ElistError läuft wie es soll.

Delphi-Quellcode:
program Project3;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.Classes, Contnrs;

type
  TGridColumn = class(Tobject)
  private
    FWidth: Integer;
    FHeight: Integer;
    FLeft: Integer;
    FTop: Integer;
  public
    constructor Create;
    property Width: Integer read FWidth write FWidth;
    property Height: Integer read FHeight write FHeight;
    property Left: Integer read FLeft write FLeft;
    property Top: Integer read FTop write FTop;
  end;

  TCustomGrid = class(Tobject)
  private
    FCols: Integer;
    FRows: Integer;
    FRowsContainer: TObjectList;

    procedure SetCols(AValue: Integer);
    procedure SetRows(AValue: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Paint;
    property Cols: Integer read FCols write SetCols;
    property Rows: Integer read FRows write SetRows;
  end;

var
  l, t, w, h: Integer;
  { TGridColumn }

constructor TGridColumn.Create;
begin
  inherited Create;
end;

{ TCustomGrid }

constructor TCustomGrid.Create;
begin
  inherited Create;
  FRowsContainer := TObjectList.Create;
end;

destructor TCustomGrid.Destroy;
begin
  FRowsContainer.Free;
  inherited;
end;

procedure TCustomGrid.SetCols(AValue: Integer);
var
  ACol, ARow: Integer;
  gridColumn: TGridColumn;
begin
  if FCols = AValue then
    Exit;
  FCols := AValue;

  for ARow := 0 to FRows - 1 do
  begin
    for ACol := 0 to FCols - 1 do
    begin
      gridColumn := TGridColumn.Create;
      gridColumn.Width := 200;
      gridColumn.Height := 20;
      gridColumn.Left := 10;
      gridColumn.Top := 10;
      TStringList(FRowsContainer.Items[ARow]).AddObject(' ', gridColumn);
    end;
  end;
end;

procedure TCustomGrid.SetRows(AValue: Integer);
var
  ARow: Integer;
  AList: TStringList;
begin
  if FRows = AValue then
    Exit;
  FRows := AValue;
 
  if FRowsContainer.Count > 0 then
    FRowsContainer.Clear; // falls mehrfacher Aufruf von SetRows Container löschen;

  for ARow := 0 to FRows - 1 do
  begin
    AList := TStringList.Create;
    AList.Capacity := 5;
    AList.Duplicates := dupAccept;
    FRowsContainer.Add(AList);
  end;
end;

procedure TCustomGrid.Paint;
var
  ACol, ARow { , r, b } : Integer;
  gc: TGridColumn;
  sl: TStringList;
  rc: TObjectList;
begin

  for ARow := 0 to FRowsContainer.Count - 1 do
  begin

    for ACol := 0 to TStringList(FRowsContainer.Items[ARow]).Count - 1 do
    begin
      rc := FRowsContainer;
      sl := TStringList(rc.Items[ARow]);
      gc := TGridColumn(sl.Objects[ACol]);
      if (ACol < TStringList(FRowsContainer.Items[ARow]).Capacity) and
        Assigned(TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol])) then
      begin
        // TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width;
        w := gc.Width;
        // TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Height;
        h := gc.Height;
        // TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Left; //aus Originalcode übernommen
        l := l + gc.Left;
        // TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Top; //aber hier weggelassen
        t := t + gc.Top;
      end;

      writeln('Top: ', t);
      writeln('Left: ', l);

      inc(t, h);
      inc(l, w);
    end;
  end;
end;

var
  Grid: TCustomGrid;

begin
  try
    { TODO -oUser -cConsole Main : Code hier einfügen }
    Grid := TCustomGrid.Create;
    Grid.Rows := 5; // zuerst Rows initialisieren dann Cols.
    Grid.Cols := 9;
    Grid.Paint;
    writeln('Zurück mit << ENTER >> > >>> ... ');
    readln;
  except
    on E: Exception do
      writeln(E.ClassName, ': ', E.Message);
  end;

end.
Zitat:

Warum müssen diese vars global definiert sein. Ich will doch lokal zeichnen?
Habe ich doch hier schon erklärt.

Wie Uwe schon schrieb..
Zitat:

Warum überhaupt dieses Hochzählen, wo die Werte doch im nächsten Durchlauf wieder gesetzt werden?
Setze l,t,w,h local und prüfe dann das Ergebnis nochmal dann verstehst du warum.
Und ignoriere keine Warnungen vom Compiler!

w, h müssen ebenfalls global definiert werden weil diese ansonsten nicht initialisiert sind.
Alternativ kannst du die auch local definieren mußt diese dann aber selbst initialisieren und zwar vor der schleife
Delphi-Quellcode:
for ARow := 0 to FRowsContainer.Count - 1 do
Delphi-Quellcode:
w := 0;
h := 0;
EDIT:
Nebenbei das kann auch nicht stimmen.
Delphi-Quellcode:
DrawRectangleClip(vscreen,l,t,l+w,t+h,ToColor(colBlack));

Das
Delphi-Quellcode:
DrawRectangleClip(vscreen,l,t,w,h,ToColor(colBlack));
ist korrekt.

Hochladen tu ich hier nichts mehr da die kompilierte EXE wieder mal als Trojan und Malware erkannt wurde.
Da die Administration so freundlich ist alle meine Projekte hier zu Untergraben spare ich mir das.

delphifan2004 22. Mai 2022 16:39

AW: EListError, warum? ObjectList mit Daten füllen
 
[QUOTE=venice2;1506171]Ok du hast Rows und Cols vertauscht.. deshalb wurde deine TStringliste nicht gefüllt in deinem Beispiel!
Versuche das hier. Habe keinen ElistError läuft wie es soll.

Delphi-Quellcode:
program Project3;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.Classes, Contnrs;

type
  TGridColumn = class(Tobject)
  private
    FWidth: Integer;
    FHeight: Integer;
    FLeft: Integer;
    FTop: Integer;
  public
    constructor Create;
    property Width: Integer read FWidth write FWidth;
    property Height: Integer read FHeight write FHeight;
    property Left: Integer read FLeft write FLeft;
    property Top: Integer read FTop write FTop;
  end;

  TCustomGrid = class(Tobject)
  private
    FCols: Integer;
    FRows: Integer;
    FRowsContainer: TObjectList;

    procedure SetCols(AValue: Integer);
    procedure SetRows(AValue: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Paint;
    property Cols: Integer read FCols write SetCols;
    property Rows: Integer read FRows write SetRows;
  end;

var
  l, t, w, h: Integer;
  { TGridColumn }

constructor TGridColumn.Create;
begin
  inherited Create;
end;

{ TCustomGrid }

constructor TCustomGrid.Create;
begin
  inherited Create;
  FRowsContainer := TObjectList.Create;
end;

destructor TCustomGrid.Destroy;
begin
  FRowsContainer.Free;
  inherited;
end;

procedure TCustomGrid.SetCols(AValue: Integer);
var
  ACol, ARow: Integer;
  gridColumn: TGridColumn;
begin
  if FCols = AValue then
    Exit;
  FCols := AValue;

  for ARow := 0 to FRows - 1 do
  begin
    for ACol := 0 to FCols - 1 do
    begin
      gridColumn := TGridColumn.Create;
      gridColumn.Width := 200;
      gridColumn.Height := 20;
      gridColumn.Left := 10;
      gridColumn.Top := 10;
      TStringList(FRowsContainer.Items[ARow]).AddObject(' ', gridColumn);
    end;
  end;
end;

procedure TCustomGrid.SetRows(AValue: Integer);
var
  ARow: Integer;
  AList: TStringList;
begin
  if FRows = AValue then
    Exit;
  FRows := AValue;
 
  if FRowsContainer.Count > 0 then
    FRowsContainer.Clear; // falls mehrfacher Aufruf von SetRows Container löschen;

  for ARow := 0 to FRows - 1 do
  begin
    AList := TStringList.Create;
    AList.Capacity := 5;
    AList.Duplicates := dupAccept;
    FRowsContainer.Add(AList);
  end;
end;

procedure TCustomGrid.Paint;
var
  ACol, ARow { , r, b } : Integer;
  gc: TGridColumn;
  sl: TStringList;
  rc: TObjectList;
begin

  for ARow := 0 to FRowsContainer.Count - 1 do
  begin

    for ACol := 0 to TStringList(FRowsContainer.Items[ARow]).Count - 1 do
    begin
      rc := FRowsContainer;
      sl := TStringList(rc.Items[ARow]);
      gc := TGridColumn(sl.Objects[ACol]);
      if (ACol < TStringList(FRowsContainer.Items[ARow]).Capacity) and
        Assigned(TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol])) then
      begin
        // TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Width;
        w := gc.Width;
        // TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Height;
        h := gc.Height;
        // TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Left; //aus Originalcode übernommen
        l := l + gc.Left;
        // TGridColumn(TStringList(FRowsContainer.Items[ARow]).Objects[ACol]).Top; //aber hier weggelassen
        t := t + gc.Top;
      end;

      writeln('Top: ', t);
      writeln('Left: ', l);

      inc(t, h);
      inc(l, w);
    end;
  end;
end;

var
  Grid: TCustomGrid;

begin
  try
    { TODO -oUser -cConsole Main : Code hier einfügen }
    Grid := TCustomGrid.Create;
    Grid.Rows := 5; // zuerst Rows initialisieren dann Cols.
    Grid.Cols := 9;
    Grid.Paint;
    writeln('Zurück mit << ENTER >> > >>> ... ');
    readln;
  except
    on E: Exception do
      writeln(E.ClassName, ': ', E.Message);
  end;

end.
Danke wie verrückt! Brett vorm Kopp gehabt, bei den vertauschten Dimensionen. Oh Mann, manchmal ist es einfach zum Verzweifeln, man findet den Fehler nicht und dann ist es so ne simple Sache. :wall: Vier Augen sehen oft mehr als nur zwei! Danke! Funktioniert nun wie es soll. :hello:

Zitat:

Zitat von venice2
w, h müssen ebenfalls global definiert werden weil diese ansonsten nicht initialisiert sind.
Alternativ kannst du die auch local definieren mußt diese dann aber selbst initialisieren und zwar vor der schleife

Das also ist es, warum die Variablen global definiert sein müssen, die Anfangsinitialisierung auch NULL! Hätte gedacht, dass diese Initialisierung auch bei likalen Variablen automatisch vorgenommen wird. Wieder was dazu gelernt.

venice2 24. Mai 2022 11:43

AW: EListError, warum? ObjectList mit Daten füllen
 
Zitat:

Das also ist es, warum die Variablen global definiert sein müssen
Einfach erklärt. (Ohne Informatiker Kauderwelsch)

Du weist(Initialisierst) einer variable den wert innerhalb einer Schleife zu.
Der Compiler erkennt das aber nicht und gibt die Warnung aus das deine variable nicht initialisiert ist.
w, h kann also einen imaginären wert enthalten.

Definierte Variablen innerhalb deiner procedure, function können sich nicht selbst initialisieren das mußt du schon selber tun wenn der Compiler die Warnung ausgibt.
Und zwar immer vor der entsprechenden Schleife innerhalb nutzt es nichts, wie schon gesagt der Compiler kennt den wert nicht.
Deshalb sollte man Warnungen nicht einfach ignorieren.

Mit t, l verhält es sich wie folgt.
Diese Variablen in der procedure, function zu initialisieren macht keinen sinn weil du diese ja hochzählen willst "Außerhalb der Funktion" deshalb muß diese global oder zumindest
innerhalb der Class definiert werden damit diese den Standard wert erhält in diesen Fall 0.
Nur außerhalb deiner procedure, function können diese Hochgezählt werden damit die neuen Werte beim nächsten Aufruf erhalten bleiben.
Innerhalb dieser wären sie wieder nicht initialisiert.

Dein Aufruf!
Delphi-Quellcode:
l := l + gc.Left;
ergibt also das Ergebnis l = 0 + 10 inkrementiert mit width l := 220 + 200 usw..
Wäre l in deiner Procedure definiert könnte das Ergebnis so aussehen l = 12345 + 10 (weil deine Variable l nicht initialisiert bzw. global definiert wurde)
Ein hochzählen der Variable l bringt nichts wenn diese nicht global definiert ist da diese innerhalb der Schleife immer wieder neu zugewiesen wird
und somit immer den wert "Irgendeine Zahl + 10" erhält.

Wenn dir irgendein Informatiker das hier besser erklären kann. Bitte schön!

EDIT:
Wenn du nicht möchtest das man von außen global auf die Variablen zugreifen kann dann addiere diese zu deiner TCustomGrid Class
Delphi-Quellcode:
  TCustomGrid = class(Tobject)
  private
    FCols: Integer;
    FRows: Integer;
    FRowsContainer: TObjectList;
    l, t, w, h: Integer; // hier!
    procedure SetCols(AValue: Integer);
    procedure SetRows(AValue: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Paint;
    property Cols: Integer read FCols write SetCols;
    property Rows: Integer read FRows write SetRows;
  end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:54 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