Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Kundenliste (https://www.delphipraxis.net/192392-kundenliste.html)

EdAdvokat 14. Apr 2017 16:55

Datenbank: SQLite-3 • Version: 3 • Zugriff über: ZeosLib

Kundenliste
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo, Ich taste mich so langsam an Datenbankanwendungen heran und habe nach vorangegangenen Übungen nun eine Erweiterung des Programms Warenverkauf geschrieben. Ich bedanke mich hiermit nochmals für die Hilfe im Forum,vor allem bei Hobbycoder, p80286 und Haentschmann, sowie weiteren usern die mir viele Tipps und Hinweise gegeben haben. Hoffentlich enttäusche ich Euch nicht mit dem jetzigen Versuch. Ich habe eine Objectlist erstellt und will mit deren Hilfe Daten in eine SQLite-3 Tabelle speichern und natürlich auch wieder auslesen.
Das hat soweit erst einmal geklappt. Die Daten wurden erstellt und in einem Listview dargestellt. Auch die Speicherung in die Datenbank klappt soweit ganz gut. Nach dem Neustart des Programms sind die automatisch erstellten Daten wieder vorhanden.
1. Frage: ich habe eine TCustomer.create erstellt. Muss ich das irgendwo auch wieder freigeben? Die Customerlist gebe ich in Destroy frei.
2. Frage: Die Datensätze werden nicht in den gesondert erstellten TEdit-Feldern angezeigt, ausgenommen die 2 am Ende der Tabelle(mit der KDNR 67). Ich kann mir nicht erklären warum und habe keinen Ansatz, um die Ursache zu ergründen. Komischerweise wird in den TEdit's nicht der entsprechende Datensatz des ListView sondern ein ganz anderer Datensatz der Tabelle angezeigt, der tatsächlich auch existiert.
Warum erscheinen die Daten nicht in den Edit-Feldern?
Also vermutlich werden nur die "fehlerhaften Datensätze" (KDNR 67) in den TEdits angezeigt. Was mache ich falsch, oder wie muss ich vorgehen?
Es handelt sich nur um ein Übungsprogramm ohne tiefen Sinn nur für mein Verständnis, um in der Sache weiter zu kommen.
Bin ich damit auf dem richtigen Weg?
Ich wünsche allen Forumnutzern ein frohes Osterfest.:thumb:

mkinzler 14. Apr 2017 17:11

AW: Kundenliste
 
Zitat:

Muss ich das irgendwo auch wieder freigeben?
Wenn die Liste der Eigentümer ist, nicht.
Zitat:

Die Customerlist gebe ich in Destroy frei.
Besser Free aufrufen.

EdAdvokat 14. Apr 2017 17:23

AW: Kundenliste
 
Create und Destroy:
Delphi-Quellcode:
procedure TFRMMainCustomer.FormCreate(Sender: TObject);
begin
  CustomerList:=TCustomerList.Create;
  connect;
end;

procedure TFRMMainCustomer.FormDestroy(Sender: TOBject);
begin
  CustomerList.Free;
end;
reicht das so?

DeddyH 14. Apr 2017 18:42

AW: Kundenliste
 
Sieht in Ordnung aus, sofern Du nicht explizit die OwnsObjects-Eigenschaft der Liste auf false gesetzt hast.

EdAdvokat 14. Apr 2017 19:08

AW: Kundenliste
 
ich frag mal ganz naiv: im listview die OwnerData-Eigenschaft im ObjectInspector steht auf false. Bei true sehe ich gar nichts.
Bin ich da an dem richtigen Punkt, oder wäre das eine andere Stellschraube? Was erreiche ich damit?

DeddyH 14. Apr 2017 19:50

AW: Kundenliste
 
Das OwnerData einer ListView besagt, ob diese im virtuellen Modus sein soll, der sehr effektiv bei großen Eintragszahlen ist. Ich meinte aber die OwnsObjects-Eigenschaft Deiner Objektliste, die festlegt, ob die enthaltenen Objekte automatisch freigegeben werden sollen, sobald sie aus der Liste entfernt werden.

EdAdvokat 14. Apr 2017 19:58

AW: Kundenliste
 
Zitat:

Das OwnerData einer ListView besagt, ob diese im virtuellen Modus sein soll, der sehr effektiv bei großen Eintragszahlen ist. Ich meinte aber die OwnsObjects-Eigenschaft Deiner Objektliste, die festlegt, ob die enthaltenen Objekte automatisch freigegeben werden sollen, sobald sie aus der Liste entfernt werden.
Da ich eher ein Greenhorn bin frage ich nochmals naiv, wo finde ich diese Einstellung OwnersObjects-Eigenschaften der Objectlist. Ich habe die Objectlist in einer gesonderten Unit (Daten-Unit gesondert erstellen, wie ich gelernt habe) erstellt und da läßt sich schwerlich was einstellen, oder sehe ich das falsch?

DeddyH 15. Apr 2017 06:00

AW: Kundenliste
 
Der Konstruktor der TObjectList hat einen Parameter, mit dem das eingestellt wird.

EdAdvokat 15. Apr 2017 07:36

AW: Kundenliste
 
Ich habe die objectlist lediglich im Hauptformular mit den Methoden erzeugt/wieder freigegeben:
Zitat:

procedure TFRMMainCustomer.FormCreate(Sender: TObject);
begin
CustomerList:=TCustomerList.Create;
connect;
end;

procedure TFRMMainCustomer.FormDestroy(Sender: TOBject);
begin
CustomerList.Free;
end;
reicht das oder muß man direkt in der Objectlist auch ein constructor create aufrufen?
Erkenntlich habe ich also überhaupt keine Schalter gesetzt. Wie und wo müßte man das tun?
Entschulige, es ist für mich Neuland.

SProske 15. Apr 2017 07:43

AW: Kundenliste
 
Beide (sowohl die nicht generische als auch die generische) TObjectList nutzen beim Aufruf
Delphi-Quellcode:
.Create
den internen Constructor

Delphi-Quellcode:
constructor Create(AOwnsObjects: Boolean = True); overload;

D.h. in diesem Falle wird der Defaultwert gezogen und der ist True. Wenn du also nirgendwo OwnsObjects der Liste setzt, steht es auf True.

In der Starter ist afaik leider kein Sourcecode enthalten, aber die Hilfe sollte das trotzdem ausspucken.

EdAdvokat 15. Apr 2017 08:53

AW: Kundenliste
 
Schau doch bitt mal drüber, ob ich das so verwenden kann oder ob ich explizit hier den constructor aufrufen muss:
Delphi-Quellcode:
unit UCustomerList;

interface

uses
classes, system.Types, system.SysUtils, ZAbstractRODataset, ZAbstractDataset,
ZDataset, ZAbstractConnection, ZConnection ,contnrs;

type
  TCustomer=class
  private
    FKDNR : Integer;
    FName: string;
    FVorname: string;
    FFirma : string;
    FProdukt: string;
    FAnzahl : Integer;
    FPreis : Currency;
    procedure SetKDNR(const Value: Integer);
    procedure SetName(const Value: string);
    procedure SetVorname(const Value: string);
    procedure SetFirma (const Value : string);
    procedure SetProdukt(const Value : String);
    procedure SetAnzahl (const Value : Integer);
    procedure SetPreis(const Value: Currency);
  published
    property KDNR: integer read FKDNr write SetKDNR;
    property Name: string read FName write SetName;
    property Vorname: string read FVorname write SetVorname;
    property Firma: string read FFirma write SetFirma;
    property Produkt: string read FProdukt write setProdukt;
    property Anzahl: Integer read FAnzahl write SetAnzahl;
    property Preis: Currency read FPreis write SetPreis;
  end;

  TCustomerList=class(TObjectList)
    function getItem(index: Integer): TCustomer; virtual;
    procedure setItem(index: Integer; Customer: TCustomer); virtual;
    procedure LoadFromDB(con: TZConnection);
    procedure SavetoDB(con: TZConnection);
  public
    property Items[index: Integer]: TCustomer read getItem write setItem; default;
    procedure Insert(index: Integer; Customer: TCustomer); virtual;
    function Add(Customer: TCustomer): Integer; virtual;
    function Remove(Customer: TCustomer): Integer; virtual;
    function IndexOf(Customer: TCustomer): Integer; virtual;
    function First: TCustomer; virtual;
    function Last: TCustomer; virtual;
    function AddCustomer(KDNR: integer; Name: string; Vorname: string; Firma: string; Produkt: string; Anzahl: Integer; Preis: Currency):integer;
  end;

implementation

  { TCustomer }

procedure TCustomer.SetAnzahl(const Value: Integer);
begin
  FAnzahl := Value;
end;

procedure TCustomer.SetFirma(const Value: string);
begin
  FFirma := Value;
end;

procedure TCustomer.SetKDNR(const Value: Integer);
begin
  FKDNR:= Value;
end;

procedure TCustomer.SetName(const Value: string);
begin
  FName := Value;
end;

procedure TCustomer.SetPreis(const Value: Currency);
begin
  FPreis := Value;
end;

procedure TCustomer.SetProdukt(const Value: String);
begin
  FProdukt := Value;
end;

procedure TCustomer.SetVorname(const Value: string);
begin
  FVorname := Value;
end;

{ TCustomerList }

function TCustomerList.Add(Customer: TCustomer): Integer;
begin
  Result:=inherited Add(Customer);
end;

procedure TCustomerList.setItem(index: Integer; Customer: TCustomer);
begin
  inherited Items[index]:=Customer;
end;

function TCustomerList.AddCustomer(KDNR: integer; Name, Vorname, Firma,
  Produkt: string; Anzahl: Integer; Preis: Currency): integer;
var
  Customer: TCustomer;
begin
  Customer:=TCustomer.Create;
  Customer.KDNR:=KDNR;
  Customer.Name:=Name;
  Customer.Vorname:=Vorname;
  Customer.Firma:=Firma;
  Customer.Produkt:=Produkt;
  Customer.Anzahl:=Anzahl;
  Customer.Preis:=Preis;
  self.Add(Customer);
end;

function TCustomerList.Remove(Customer: TCustomer): Integer;
begin
  Result:=inherited Remove(Customer);
end;

function TCustomerList.First: TCustomer;
begin
  Result:=TCustomer(inherited First());
end;

function TCustomerList.getItem(index: Integer): TCustomer;
begin
  Result:=TCustomer(inherited Items[index]);
end;

function TCustomerList.IndexOf(Customer: TCustomer): Integer;
begin
  Result:=inherited IndexOf(Customer);
end;

procedure TCustomerList.Insert(index: Integer; Customer: TCustomer);
begin
  inherited Insert(index, Customer);
end;

function TCustomerList.Last: TCustomer;
begin
  Result:=TCustomer(inherited Last());
end;

procedure TCustomerList.LoadFromDB(con: TZConnection);
var
  zqyMain: TZQuery;
  Customer: TCustomer;
begin
  zqyMain:=TZQuery.Create(nil);
  Try
    self.Clear; //Dank TObjectlist werden auch alle bereits vorhandenen Objecte automatisch freigegeben
    zqyMain.connection:=con;
    zqyMain.sql.text:='SELECT * FROM WARENVERKAUF1';
zqyMain.active:=True;
while not zqyMain.eof do
begin
  Customer:=TCustomer.Create;
  Customer.KDNR:=zqyMain.fieldbyname('KDNR').AsInteger;
  Customer.Name:=zqyMain.fieldbyname('Name').AsString;
  Customer.Vorname:=zqyMain.fieldbyname('Vorname').AsString;
  Customer.Firma:=zqyMain.fieldByName('Firma').AsString;
  Customer.Produkt:=zqyMain.fieldbyname('Produkt').AsString;
  Customer.Anzahl:=zqyMain.FieldByName('Anzahl').AsInteger;
  Customer.Preis:=zqyMain.fieldbyname('Preis').AsCurrency;
  self.Add(Customer);
end;
  zqyMain.active:=False;
Finally
  zqyMain.free;
End;
end;

procedure TCustomerList.SavetoDB(con: TZConnection);
var
  zqryMain: TZQuery;
  i: Integer;
begin
  zqryMain:=TZQuery.Create(nil);
  try
    zqryMain.connection:=con;
    zqryMain.sql.text:='INSERT INTO WARENVERKAUF1 (KDNR, NAME, VORNAME, FIRMA, PRODUKT, ANZAHL, PREIS) VALUES (:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)';
    zqryMain.params.parseSQL(zqryMain.sql.text, True);
for i:=0 to self.count-1 do
begin
  zqryMain.params.ParamValues['KNR']:=self[i].KDNR;
  zqryMain.params.paramValues['NAM']:=self[i].Name;
  zqryMain.params.paramValues['VNA']:=self[i].Vorname;
  zqryMain.params.ParamValues['FIR']:=self[i].Firma;
  zqryMain.Params.ParamValues['PRO']:=self[i].Produkt;
  zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl;
  zqryMain.params.paramValues['PRE']:=self[i].Preis;
  zqryMain.ExecSQL;
end;
finally
  zqryMain.free;
end;
end;
end.
Bislang habe ich die Objectlist lediglich im Formular erzeugt.
Habe zwischenzeitlich nun auch die D10.2 Tokyo prof. und könnte daher wo nachsehen? Source von objectlist ?
Ich habe immer noch die Frage danach warum die Daten nicht auch in den TEdits im Hauptformular angezeigt werden.:?:

SProske 15. Apr 2017 09:05

AW: Kundenliste
 
Da du ja ein modernes Delphi hast - wieso verwendest du keine generische TObjectList. Damit würdest du dir das redeklarieren der ganzen TObjectList-Funktionalitäten sparen und deine TCustomerList sähe nur noch so aus (ohne IDE heruntergetippt kopiert und ungetestet):

Delphi-Quellcode:
  TCustomerList=class(TObjectList<TCustomer>)
    procedure LoadFromDB(con: TZConnection);
    procedure SavetoDB(con: TZConnection);
  public
    function AddCustomer(KDNR: integer; Name: string; Vorname: string; Firma: string; Produkt: string; Anzahl: Integer; Preis: Currency):integer;
  end;
Und ja, da du das Create nicht überschreibst, rufst du das Create der Vorgängerklasse (TObjectList) auf.

Und ja, der Sourcecode könnte ebenfalls erleuchtend wirken. Ein Strg+Click auf TCustomerList.Create sollte dich an die richtige Stelle führen.

EdAdvokat 15. Apr 2017 11:57

AW: Kundenliste
 
Leider erhellt sich bei mir wenig. Sicher fehlen mir noch immer einige Grundlagen.
1.Könnte ich also die gepostete unit so verwenden, wie sie ist ohne expliziten Aufruf des constructors?
2.Das Thema generische Listen ist mir völlig neu und muss ich erst einmal recherchieren. Habe mir zwischenzeitlich die unit contnrs angesehen.
Wenn ich nun den Vorschlag von SProske aufgreife scheitere ich bereits bei den < und > bei <TCustomer>. Da meckert ganz Tokyo.
Nehme ich den Passus raus will er doch einiges Gedöns aus der unit contnrs.
So lasse ich es erst einmal mit meinem redundanten Code, bis ich mich schlauer gemacht habe.
3. gibt es ein Tutorial zum Thema generischen Listen?

SProske 15. Apr 2017 12:09

AW: Kundenliste
 
1. Ja, entscheidend ist der Aufruf von
Delphi-Quellcode:
TCustomerList.Create
, wo auch immer das dann tatsächlich geschieht.
2. Du musst die unit System.Generics.Collections einbinden.

EdAdvokat 15. Apr 2017 16:38

AW: Kundenliste
 
Hallo,
Zitat:

Da du ja ein modernes Delphi hast - wieso verwendest du keine generische TObjectList. Damit würdest du dir das redeklarieren der ganzen TObjectList-Funktionalitäten sparen und deine TCustomerList sähe nur noch so aus
Das hat nun geklappt und in der Tat ist der Quelltext erheblich kürzer und es funktioniert sogar:thumb:
Doch wenn ich die Daten lade rödelt meine Maschine und teilt mir dann schlussendlich mit, "zuwenig Arbeitspeicher" obwohl ich 16 MB habe und nichts speicherfressendes läuft. Da ist doch was faul? Wo könnte ich da suchen?

DeddyH 15. Apr 2017 17:53

AW: Kundenliste
 
Du liest immer denselben Datensatz ein, da Du nie auf den nächsten wechselst. Soll heißen, da fehlt ein Next in Deiner Schleife.

EdAdvokat 15. Apr 2017 21:05

AW: Kundenliste
 
Danke erst einmal für die Hilfe. Die Anzeige in den Edit-Feldern klappt nunmehr. Will ich jedoch einen Datensatz löschen oder bearbeiten erhalte ich die Fehlermeldung "Argument außerhalb des Bereiches". Also in beiden Fällen die gleiche Meldung. Wo sollte ich nun nach welchem Problem suchen. Blöd ist nur, dass ein F1 nicht weiterhilft und der Fehler nur beim Click auf die jeweiligen Button, also beim Aufruf der Methoden erscheint. Was möge denn gemeint sein mit "...außerhalb des Bereiches.."?
Dann habe ich mir die Methode LoadfromDB angesehen, die dazu führt, dass nicht genügend Arbeitsspeicher vorhanden sei. Da kann ich jedoch nichts feststellen.
Delphi-Quellcode:
procedure TCustomerList.LoadFromDB(con: TZConnection);
var
  zqyMain: TZQuery;
  Customer: TCustomer;
begin
  zqyMain:=TZQuery.Create(nil);
  Try
    self.Clear; //Dank TObjectlist werden auch alle bereits vorhandenen Objecte automatisch freigegeben
    zqyMain.connection:=con;
    zqyMain.sql.text:='SELECT * FROM WARENVERKAUF1';
zqyMain.active:=True;
while not zqyMain.eof do
begin
  Customer:=TCustomer.Create;
  Customer.KDNR:=zqyMain.fieldbyname('KDNR').AsInteger;
  Customer.Name:=zqyMain.fieldbyname('Name').AsString;
  Customer.Vorname:=zqyMain.fieldbyname('Vorname').AsString;
  Customer.Firma:=zqyMain.fieldByName('Firma').AsString;
  Customer.Produkt:=zqyMain.fieldbyname('Produkt').AsString;
  Customer.Anzahl:=zqyMain.FieldByName('Anzahl').AsInteger;
  Customer.Preis:=zqyMain.fieldbyname('Preis').AsCurrency;
  self.Add(Customer);
end;
  zqyMain.active:=False;
Finally
  zqyMain.free;
End;
end;
Da hänge ich also fest.

DeddyH 16. Apr 2017 06:47

AW: Kundenliste
 
Ich hatte es doch geschrieben: sobald auch nur ein Datensatz enthalten ist, wird Eof nie eintreten, solange Du Deine Datenmenge nicht mit Next auf den nächsten Datensatz positionierst.

EdAdvokat 16. Apr 2017 19:11

AW: Kundenliste
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen, es ist mein erstes Projekt dieser Dimension und ich komme einfach nicht weiter und verzweifele, da ich bereits 2 Tage vergeblich den Fehler in diesem kleinen Übungsprogramm suche.
Nach dem Aufruf des Programms wird die SQLLite-Tabelle in einem Listview (auch mit ID!)exakt angezeigt und auch die TEdit-Felder sind parallel dazu zu sehen. Will ich jedoch einen Datensatz daraus löschen oder bearbeiten (entspr. Methoden per ButtonClick) kommt die Meldung
"Argument außerhalb des Bereiches". Will ich einen Datensatz hinzufügen oder auch mit DBSpeichern kommt: "Operation bei geschlossener Datenmenge nicht ausführbar" .
Nach dem Aufruf von Bearbeiten rückt die Anzeige im Listview nach links d.h. das Feld ID wird nun mit der KDNR belegt. Also die ID ist nicht mehr sichtbar und dafür in der Spalte wird die Kundennummer angezeigt und folgend Name bei KDNR usw.
Ich habe bereits alles versucht, was mir eingefallen ist, doch ich kam zu keinem vernünftigen Ergebnis.
Die Anzeige der Daten in den Edit-Feldern im FRMMain ist ebenfalls weg. Es wird wohl irgendwo was unterschlagen, vermutlich die ID.
Wo könnte ich suchen oder was müßte ich tun? Debugger.. na ja brachte mich nicht weiter, da mir wohl die Erfahrung fehlt.
Es ist für mich wirklich völliges Neuland und ich habe mich wirklich bemüht, doch jetzt stehe ich in dieser Sache vermutlich vor dem Aus, doch ich glaube es ist nur ein verdammter Denkfehler von mir.
Sollte sich jemand erbarmen, mir einen Tipp zu geben, wäre ich sehr dankbar. Anbei das Programm in der jetzigen Fassung und die beiden entscheidenen units:
Delphi-Quellcode:
unit UCustomerList;

interface

uses
classes, system.Types, system.SysUtils, ZAbstractRODataset, ZAbstractDataset,
ZDataset, ZAbstractConnection, ZConnection ,contnrs, System.Generics.Collections;

type
  TCustomer=class
  private
   // FID: integer;
    FKDNR : Integer;
    FName: string;
    FVorname: string;
    FFirma : string;
    FProdukt: string;
    FAnzahl : Integer;
    FPreis : Currency;
   // procedure SetID(const Value: Integer);
    procedure SetKDNR(const Value: Integer);
    procedure SetName(const Value: string);
    procedure SetVorname(const Value: string);
    procedure SetFirma (const Value : string);
    procedure SetProdukt(const Value : String);
    procedure SetAnzahl (const Value : Integer);
    procedure SetPreis(const Value: Currency);
  published
   // property ID: integer read FID write SetID;
    property KDNR: integer read FKDNr write SetKDNR;
    property Name: string read FName write SetName;
    property Vorname: string read FVorname write SetVorname;
    property Firma: string read FFirma write SetFirma;
    property Produkt: string read FProdukt write setProdukt;
    property Anzahl: Integer read FAnzahl write SetAnzahl;
    property Preis: Currency read FPreis write SetPreis;
  end;

  TCustomerList=class(TObjectList<TCustomer>)//generische Liste (contnrs, System.Generics.Collections)
    procedure LoadFromDB(con: TZConnection);
    procedure SavetoDB(con: TZConnection);
  public
    function AddCustomer(KDNR: integer; Name: string; Vorname: string; Firma: string; Produkt: string; Anzahl: Integer; Preis: Currency):integer;
  end;

implementation

  { TCustomer }

procedure TCustomer.SetAnzahl(const Value: Integer);
begin
  FAnzahl := Value;
end;

procedure TCustomer.SetFirma(const Value: string);
begin
  FFirma := Value;
end;

{procedure TCustomer.setID(const Value: Integer);
begin
  FID := Value;
end;  }

procedure TCustomer.SetKDNR(const Value: Integer);
begin
  FKDNR:= Value;
end;

procedure TCustomer.SetName(const Value: string);
begin
  FName := Value;
end;

procedure TCustomer.SetPreis(const Value: Currency);
begin
  FPreis := Value;
und weiter
Delphi-Quellcode:
//Programm Kundenliste als ersten Versuch eine Objectlist zur Aufnahme der Daten
//für eine SQLite DB einzusetzen.
//Hilfe dazu erhielt ich aus vorangegangenen Projekten in der DP vor allem von
//Hobbycoder, Haentschmann und P80286, dennen ich hiermit nochmals danke.
//Erstellt von EdAdvokat April 2017
unit uMainFRM;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, uFRMCustomer,
  uCustomerList, Vcl.ComCtrls, Data.DB, ZAbstractRODataset, ZAbstractDataset,
  ZDataset, ZAbstractConnection, ZConnection;

type
  TFRMMainCustomer = class(TForm)
    lvCustomer: TListView;
    btnAdd: TButton;
    btnMake: TButton;
    btnDelete: TButton;
    btnAutomatic: TButton;
    btnClose: TButton;
    conMain: TZConnection;
    zqryMain: TZQuery;
    btnSaveDB: TButton;
    btnloadDB: TButton;
    edtKDNR: TEdit;
    edtName: TEdit;
    edtVorname: TEdit;
    edtFirma: TEdit;
    edtProdukt: TEdit;
    edtAnzahl: TEdit;
    edtPreis: TEdit;
    lblKDNR: TLabel;
    lblName: TLabel;
    lblVorname: TLabel;
    lblFirma: TLabel;
    lblProdukt: TLabel;
    lblAnzahl: TLabel;
    lblPreis: TLabel;
    edtTestID: TEdit;
    procedure btnAddClick(Sender: TObject);
    procedure btnMakeClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
    procedure btnAutomaticClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TOBject);
    procedure btnCloseClick(Sender: TObject);
    procedure btnSaveDBClick(Sender: TObject);
    procedure btnloadDBClick(Sender: TObject);
    procedure lvCustomerClick(Sender: TObject);

  private
    { Private-Deklarationen }
    CustomerList: TCustomerList;
    procedure FuelleListView;
    procedure connect;
    procedure clearAllFields;
    procedure saveTableData(ID,KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS:string );
  public
    { Public-Deklarationen }
  end;

var
  FRMMainCustomer: TFRMMainCustomer;

implementation

{$R *.dfm}

procedure TFRMMainCustomer.btnAutomaticClick(Sender: TObject);
var
  r: integer;
  I: Integer;
  KDN, KDN1, Name, Vorname, Firma, Produkt: string;
  KDNR, Anzahl: Integer;
  Preis: Currency;
begin
  Customerlist.Clear;
  Randomize;
  for I := 0 to 999 do
  begin
    r:=Random(10);
    case r of
      0: Name:='Lehmann';
      1: Name:='Krausnig';
      2: Name:='Karlsberg';
      3: Name:='Ludwig';
      4: Name:='Antonia';
      5: Name:='Delphenhorst';
      6: Name:='Erinig';
      7: Name:='Fiedler';
      8: Name:='Gurrlig';
      9: Name:='Henzener';
    end;
    r:=Random(10);
    case r of
      0: Vorname:='Dieter';
      1: Vorname:='Klaus';
      2: Vorname:='Paul';
      3: Vorname:='Helga';
      4: Vorname:='Bernd';
      5: Vorname:='Friedel';
      6: Vorname:='Gustaf';
      7: Vorname:='Heinz';
      8: Vorname:='Joachim';
      9: Vorname:='Manfred';
    end;
    r:=Random(10);
    case r of
      0: KDN:='11';
      1: KDN:='12';
      2: KDN:='13';
      3: KDN:='14';
      4: KDN:='15';
      5: KDN:='16';
      6: KDN:='17';
      7: KDN:='18';
      8: KDN:='19';
      9: KDN:='20';
    end;
    r:=Random(10);
    case r of
      0: KDN1:='21';
      1: KDN1:='22';
      2: KDN1:='23';
      3: KDN1:='24';
      4: KDN1:='25';
      5: KDN1:='26';
      6: KDN1:='27';
      7: KDN1:='28';
      8: KDN1:='29';
      9: KDN1:='30';
    end;
    r:=Random(10);
    case r of
      0: Firma:='Schulbedarf';
      1: Firma:='Schule&mehr';
      2: Firma:='Hausmeisterserv.';
      3: Firma:='Lernen KG';
      4: Firma:='Lehrbuch Co.';
      5: Firma:='Möbel KG';
      6: Firma:='Hefte/Bücher';
      7: Firma:='Buch4you';
      8: Firma:='Lernen4all';
      9: Firma:='Bedarf Schule';
    end;
    r:=Random(10);
    case r of
      0: Produkt:='Stühle';
      1: Produkt:='Tische';
      2: Produkt:='Hefte';
      3: Produkt:='Blöcke';
      4: Produkt:='Bilder';
      5: Produkt:='Kreide';
      6: Produkt:='Bücher';
      7: Produkt:='Tafeln';
      8: Produkt:='Werkzeug';
      9: Produkt:='Mappen';
    end;
    KDNR:=strtoint(KDN[1]+KDN1[1]+format('%.6d',[i]));
    Anzahl:=Random(20);
    r:=Random(10000);
    Preis:=r/100;
    CustomerList.AddCustomer(KDNR, Name, Vorname, Firma, Produkt, Anzahl, Preis );
  end;
  FuelleListView;
end;

procedure TFRMMainCustomer.btnCloseClick(Sender: TObject);
begin
  close;
end;

procedure TFRMMainCustomer.btnAddClick(Sender: TObject);
var
  Customer: TCustomer;
begin
  //Eingabeformular mit Defaultwerten bestücken

  FRMCustomer.edtKDNR.Text:='';
  FRMCustomer.edtName.Text:='';
  FRMCustomer.edtName.Text:='';
  FRMCustomer.edtVorname.text:='';
  FRMCustomer.edtFirma.text:='';
  FrmCustomer.edtProdukt.Text:='';
  FrmCustomer.edtAnzahl.text:='';
  FRMCustomer.edtPreis.text:='';

  if frmCustomer.ShowModal=mrOK then
  begin
    Customer:=TCustomer.Create;  //Das Object Customer erzeugen.
    Customer.KDNR:=strtoint(frmCustomer.edtKDNR.Text); //Customer wird befüllt
    Customer.Name:=FRMCustomer.edtName.Text;
    Customer.Vorname:=FRMCustomer.edtVorname.text;
    Customer.Firma:=FRMCustomer.edtFirma.text;
    Customer.Produkt:=FRMCustomer.edtProdukt.text;
    customer.Anzahl:=strtoint(FRMCustomer.edtAnzahl.text);
    Customer.Preis:= StrToCurr(FRMCustomer.edtPreis.Text);
    CustomerList.Add(Customer);                //Übergabe an CustomerList
    FuelleListView;
  end;
end;

procedure TFRMMainCustomer.btnDeleteClick(Sender: TObject);
begin
   if lvCustomer.Selected<>nil then
  begin
    //Hier löschen wir das Object aus der ObjectListe
    //Durch die Eigenschaft OwnObjects, die zu jeder TObjectList gehört,
    //kümmert sich die ObjectListe selber darum, das entsprechende Object Customer
    //frei zugehen. Brauchen wir also nicht selber mache.
    //Ansonsten gilt grundsätzlich, was ich selber erzeuge gebe ich auch selber wieder frei
    CustomerList.Delete(lvCustomer.Selected.Index);
    FuelleListView;
  end;
end;

procedure TFRMMainCustomer.btnloadDBClick(Sender: TObject);
begin
  CustomerList.LoadFromDB(conMain);
end;

procedure TFRMMainCustomer.btnSaveDBClick(Sender: TObject);
begin
  CustomerList.SavetoDB(conMain);
end;

procedure TFRMMainCustomer.btnMakeClick(Sender: TObject);
begin
   if lvCustomer.Selected<>nil then
  begin
    //Über den Index kann man nun auf jedes Object in der ObjektListe zugreifen
    //Damit bestücken wir das Formular mit den Werten aus den Eigenschaften des Objects
    frmCustomer.edtKDNR.Text:=inttostr(CustomerList[lvCustomer.Selected.Index].KDNR);
    frmCustomer.edtName.Text:=CustomerList[lvCustomer.Selected.Index].Name;
    frmCustomer.edtVorname.Text:=CustomerList[lvCustomer.Selected.Index].Vorname;
    fRMCustomer.edtFirma.Text:=CustomerList[lvCustomer.Selected.Index].Firma;
    frmCustomer.edtProdukt.text:=CustomerList[lvCustomer.Selected.Index].Produkt;
    frmCustomer.edtAnzahl.Text:=IntToStr(Customerlist[lvCustomer.Selected.Index].Anzahl);
    frmCustomer.edtPreis.Text:=CurrToStr(Customerlist[lvCustomer.Selected.Index].Preis);

    if FRMCustomer.ShowModal=mrOK then
    begin
      //Object erzeugen nicht nötig, da es bereits besteht. Zugriff also möglich
      //für die Übertragung der Daten aus dem Formular
      CustomerList[lvCustomer.Selected.Index].KDNR:=strtoint(frmCustomer.edtKDNR.Text);
      CustomerList[lvCustomer.Selected.Index].Name:=frmCustomer.edtName.Text;
      CustomerList[lvCustomer.Selected.Index].Vorname:=FRMCustomer.edtVorname.Text;
      CustomerList[lvCustomer.Selected.Index].Firma:=FRMCustomer.edtFirma.text;
      CustomerList[lvCustomer.Selected.Index].Produkt:=FRMCustomer.edtProdukt.text;
      CustomerList[lvCustomer.Selected.Index].Anzahl:=strtoint(FRMCustomer.edtAnzahl.text);
      CustomerList[lvCustomer.Selected.Index].Preis:=strtocurr(FRMCustomer.edtPreis.Text);
      FuelleListView;
    end;
  end;
end;

procedure TFRMMainCustomer.clearAllFields;
begin
  with FRMMainCustomer do
  begin
    edtKDNR.Clear;
    edtName.Clear;
    edtVorname.Clear;
    edtFirma.Clear;
    edtProdukt.Clear;
    edtAnzahl.Clear;
    edtPreis.Clear;
  end;
end;

procedure TFRMMainCustomer.connect;
begin
  conMain.LibraryLocation:=ExtractFilePath(application.ExeName)+'sqlite3.dll';
  conMain.Database:=ExtractFilePath(application.ExeName)+'WarenVK.sqlite';
  conMain.Connected:=true;

  zqryMain.SQL.Clear;
  zqryMain.Params.Clear;

  zqryMain.SQL.Text:='SELECT * FROM WARENVERKAUF1';
  zqryMain.Open;
  while not zqryMain.Eof do
  begin
    saveTableData(zqryMain.FieldByName('ID').AsString,zqryMain.FieldByName('KDNR').AsString,zqryMain.FieldByName('Name').AsString, zqryMain.FieldByName('Vorname').AsString,zqryMain.FieldByName('Firma').AsString,zqryMain.FieldByName('Produkt').AsString,zqryMain.FieldByName('Anzahl').AsString, zqryMain.FieldByName('Preis').AsString);
    zqryMain.Next
  end;
  zqryMain.Close;
end;

procedure TFRMMainCustomer.FormCreate(Sender: TObject);
begin
  CustomerList:=TCustomerList.Create;
  connect;
  clearAllFields;
end;

procedure TFRMMainCustomer.FormDestroy(Sender: TOBject);
begin
  CustomerList.Free;
end;

procedure TFRMMainCustomer.FuelleListView;
var
  li: TListItem;
  I: Integer;
begin
  lvCustomer.Items.BeginUpdate;
  Try
    //ListView mit Daten aus der ObjectList befüllen.
    lvCustomer.Items.Clear;
    for I := 0 to CustomerList.Count-1 do
    begin
      li:=lvCustomer.Items.Add;
      li.Caption:=inttostr(CustomerList[i].KDNR);
      li.SubItems.Add(CustomerList[i].Name);
      li.SubItems.Add(CustomerList[i].Vorname);
      li.SubItems.Add(CustomerList[i].Firma);
      li.SubItems.Add(CustomerList[i].Produkt);
      li.SubItems.Add(inttostr(CustomerList[i].Anzahl));
      li.SubItems.Add(FormatCurr('#0.00 €', CustomerList[i].Preis));
    end;
  Finally
    lvCustomer.Items.EndUpdate;
  End;
end;

procedure TFRMMainCustomer.lvCustomerClick(Sender: TObject);
var CurrentCustomerID : string;
begin
  if lvCustomer.SelCount>=1 then
  begin
    CurrentCustomerID:=lvCustomer.Selected.Caption; //
    zqryMain.SQL.Clear;
    zqryMain.Params.Clear;
    zqryMain.SQL.Text:='SELECT * FROM WARENVERKAUF1 WHERE ID = :CID';
    // WHERE ID='+#39+ID+#39;
    zqryMain.ParamByName('CID').AsString := CurrentCustomerID; //
    zqryMain.Open;

    edtKDNR.text:=zqryMain.FieldByName('KDNR').AsString;
    edtName.text:=zqryMain.FieldByName('Name').AsString;
    edtVorname.Text:=zqryMain.FieldByName('Vorname').AsString;
    edtFirma.text:=zqryMain.FieldByName('Firma').asstring;
    edtProdukt.text:=zqryMain.FieldByName('Produkt').AsString;
    edtAnzahl.text:=zqryMain.FieldByName('Anzahl').AsString;
    edtPreis.Text:=zqryMain.FieldByName('Preis').AsString;
    zqryMain.Next; //
    zqryMain.close;
  end;
end;

procedure TFRMMainCustomer.saveTableData(ID, KDNR, NAME, VORNAME, FIRMA, PRODUKT, ANZAHL, PREIS: string);
var item:TListItem;
begin
  Item:=lvCustomer.Items.Add;
  item.Caption:=ID;        //
  item.SubItems.Add(KDNR);     //
  item.SubItems.Add(Name);
  item.SubItems.Add(Vorname);
  item.SubItems.Add(Firma);
  item.SubItems.Add(Produkt);
  item.SubItems.Add(Anzahl);
  item.SubItems.Add(Preis);
end;

end.
ich wäre sehr dankbar für eine Meinungsäußerung, damit ich das ganze nicht wirklich in die Tonne treten muss.

p80286 17. Apr 2017 10:55

AW: Kundenliste
 
Hallo EdAdvokat
nicht in die Tonne kloppen, aber den Editor schließen und mit Papier und Bleistift ein Konzept erstellen.
Was willst Du?
Kundendaten verwalten.
was sind Kundendaten und was verstehst Du unter Verwalten?
Ich würde es jetzt so angehen, das das Verwalten in Delpi programmiert wird, also Du zunächst anzeigen willst. Dann vorhandene Daten ändern und neue Daten erfassen. Benötigst Du hierfür eine Darstellung oder drei unterschiedliche? Usw.
Nur solange es eben geht, vermeide es ein konkretes Feld (Name,Ort etc.) zu nutzen.
Der andere, nicht der erste oder zweite, ist es die Daten zu definieren und der DB eine Struktur zu geben.
Es ist für die die Daten vollkommen unerheblich wie die Benutzeroberfläche aussieht, und für die Oberfläche ist es egal wie sie an die Daten kommt. Wenn Du z.B. durch eine Datenmenge scrollen willst, ist das meiner Meinung nach eine Aufgabe der Oberfläche, genauso kannst Du aber jeden Datensatz einzeln abrufen, das wäre dann das Design der Schnittstelle.

So solltest Du Dir langsam erarbeiten was Du willst, bzw. was Dein Programm leisten soll.
Und wenn Du das Konzept erstellt hast, dann kannst Du daran gehen zu coden.

Gruß
K-H

EdAdvokat 17. Apr 2017 12:47

AW: Kundenliste
 
Hallo K-H, es hört sich alles so einfach an. Ich finde einfach keinen Ansatzpunkt, den blöden Fehler oder die Fehler im Programm einzugrenzen geschweige denn sie zu beheben.
Doch nun zu Deinem Vorschlag, die Sache konzeptionell anzugehen. Ich möchte für mich beispielhaft eine Kundenliste über ein Eingabeformular mit Editfeldern erstellen, diese Daten dann in einem Listview und auch in Edit's eines Hauptformulars anzeigen lassen.
Diese Daten sollen in einer SQLite-3 Datenbanktabelle gespeichert werden.
Es soll möglich sein, neue Datensätze hinzuzufügen, die bestehenden Daten zu bearbeiten (ggf. zu korrigieren oder neue hinzuzufügen) und auch Datensätze zu löschen. Die Tabelle soll folgende Felder haben: ID, KDNR, Name, Vorname, Firma, Produkt, Anzahl, Preis.
All dies soll mittels Objectlist umgesetzt werden.
Da ich davon ausgehe, dass es nicht unbedingt erforderlich ist, die hinzugefügten/veränderten/gelöschten Daten explicit zu speichern oder aufzurufen, könnte auf die Methoden SavetoDB und LoadFromDB verzichtet werden.
Das sind also meine Vorstellungen und insoweit (außer den noch enthaltenen SavetoDB/LoadFromDB-Methoden) bereits im vorhandenen Projekt umgesetzt.
Bis hierher d'accord ?
Wie weiter? Ich habe mich an das Bestehende mühseelig herangearbeitet, was heißen soll über mehrere Schritte vergangener Projekte ohne Datenbankanbindung und mit einer Datenspeicherung per record in eine dat-Datei über den Hinweis auf eine objectlist hin zu dieser ersatzweise zum record und mit einer SQLite-3 Anbindung (beispielsweise).
Stand der Dinge: Hauptformular mit Listview und TEdit-Feldern (nur so für mich, um zu sehen, dass man die Daten auch so darstellen könnte) und Buttons für Aufruf eines Unterformulars "Hinzufügen" mit Editfeldern für KDNR, Name, Vorname, Firma, Produkt, Anzahl, Preis.
Dann weiter im Hauptformular bearbeiten-Methodenaufruf und Löschen-Methodenaufruf. Das war es eigentlich schon.
In der jetzigen Fassung sind also noch die beiden Methoden LoadFromDB und SaveToDB sowie die Möglichkeit 100 Datensätze automatisch zu erzeugen enthalten. Diese 3 wären sicher entbehrlich.
Der Aufruf des Programms läßt im Listview erkennen, dass die Daten mit ID,KDNR,Name,Vorname,Firma,Produkt,Anzahl,Preis exakt erscheinen.
Das war es schon mit den positiven Überraschungen. Denn Hinzufügen ruft zwar das Unterformular auf und ich kann neue Daten eingeben und das Formular schließen. Dann erscheint nur der neue Datensatz und nach dem Neuaufruf des Programms ist dieser jedoch nicht
enthalten. Er wurde also nicht gerspeichert.Bei Aufruf von bearbeiten und löschen erscheint: "Argument außerhalb des Bereiches", was immer das auch bedeuten soll.
Das ist der Stand der Dinge.

Hobbycoder 17. Apr 2017 13:23

AW: Kundenliste
 
Im Grunde bist du doch schon recht weit. Nur die SaveToDB-Routine läuft noch nicht so, wie du es dir vorgestellt hast (wenn ich das richtig verstanden habe).

Es gibt ja grundsätzlich 2 Möglichkeiten mit Daten in einer DB zu arbeiten.
1. Mit laufender Verbindung zu der Tabelle, z.B. über ein geöffnetes TQuery oder TTable und über ein DataSource das ganze an Datensensitive Componenten zu binden z.B: DBEdit, DBCompobox, etc.
Dann kann man über Query.Edit einen Datensatz editieren. Die angebundenen Controls erhalten dann automatisch die Möglichkeit deren Daten zu ändern. Und über Query.POst btw. Query.Cancel das Editieren zu beenden. Gleiches würde z.B. über Query.Append mit neuen Datensätzen gehen.
Wie das dann genau unter Zeos aussieht, müsstest du dir erarbeiten

2. So wie du es jetzt hast. Daten komplett in eine ObjectList einlesen. Dann Objekte Bearbeiten, löschen oder hinzufügen und wieder komplett speichern.
Doch beim Speichern stellt sich dann aber die Frage: Ist der Datensatz nun neu oder ist er nur editiert? Davon ist nämlich abhängig, ob du beim Speichern über SQL das mit Insert oder mit Update machst.
Ich mache das immer folgerndermaßen. Man kann eigentlich in der SQL-DB in den Tabellen ein Datenfald mit Autoincrement versehen. Oft auch Lfdnr (laufende Nummer) oder ID benannt. Jeder Datensatz erhält darüber eine eindeutige Nummer innerhalb seiner Tabelle. Diese Nummer wird nicht noch einmal vergeben, auch wenn der Datensatz gelöscht wird. Und sie wird automatisch vergeben, also braucht/darf sie beim Insert nicht angegeben werden.
So, nun würde ich in den Objecten diese ID als Integer mit aufnehmen und beim LoadFromDB mit laden. Außerdem würde ich den Objecten eine Constructor spendieren in dem diese ID mit -1 vorbelegt ist.
Delphi-Quellcode:
Type
Tblabla=class
  private
    FID: Integer;
    procedure SetID(value: Integer);
  public
    constructor Create;
  published
    property ID: Integer read FID write SetID;
end;

constructor Tblabla.Create;
begin
  inherited;
  self.FID:=-1;
end;
Nun erhält jedes neue Object der Objectliste in ID den Wert -1.
Beim LoadFromDB wird ja auch für jeden Datensatz ein Object erzeugt, aber genau dort wird auch die ID aus der DB geladen, also hat ID dann der autoincrement-wert aus der DB und nicht mehr -1.

Wenn du nun in der Objektliste ein neues Object anlegst, bekommt es aber automatisch als ID erst einmal -1.
Jetzt kannst du bei SaveToDB anhand der ID wunderbar unterscheiden, ob ein Insert oder ein Update notwendig ist:
Delphi-Quellcode:
Procedure TblablaList.SaveToDB(....);
begin
  for i:=0 to blablaList.Count-1 do
  begin
    if blablaList[i].ID=-1 then
      q.sql.text:='Insert into Kundenliste (Name, Vorname) VALUES (:name, :Vorname)' eise
      q.sql.text:='Update Kundenliste SET Name=:name, Vorname=:Vorname where ID=:id';
   ....
  end;
end;
Möglicherweise hilft dir das etwas weiter.
Gruß Hobbycoder

EdAdvokat 17. Apr 2017 15:36

AW: Kundenliste
 
Danke zunächst. Habe hoffentlich richtig Deine Hinweise umgesetzt:
Delphi-Quellcode:
type
  TCustomer=class
  private
    FID: integer;
    FKDNR : Integer;
    FName: string;
    FVorname: string;
    FFirma : string;
    FProdukt: string;
    FAnzahl : Integer;
    FPreis : Currency;
    procedure SetID(const Value: Integer);
    procedure SetKDNR(const Value: Integer);
    procedure SetName(const Value: string);
    procedure SetVorname(const Value: string);
    procedure SetFirma (const Value : string);
    procedure SetProdukt(const Value : String);
    procedure SetAnzahl (const Value : Integer);
    procedure SetPreis(const Value: Currency);
  public
    constructor Create;

  published
    property ID: integer read FID write SetID;
    property KDNR: integer read FKDNr write SetKDNR;
    property Name: string read FName write SetName;
    property Vorname: string read FVorname write SetVorname;
    property Firma: string read FFirma write SetFirma;
    property Produkt: string read FProdukt write setProdukt;
    property Anzahl: Integer read FAnzahl write SetAnzahl;
    property Preis: Currency read FPreis write SetPreis;
  end;
und weiter:
Delphi-Quellcode:
constructor TCustomer.Create;
begin
  inherited;
  self.FID:=-1;
end;
Methode SaveToDB:
Delphi-Quellcode:
procedure TCustomerList.SavetoDB(con: TZConnection);
var
  zqryMain: TZQuery;
  i: Integer;
  CustomerListe: TCustomerList;
begin
  zqryMain:=TZQuery.Create(nil);
  try
    zqryMain.connection:=con;
    zqryMain.sql.text:='INSERT INTO WARENVERKAUF1 (KDNR, NAME, VORNAME, FIRMA, PRODUKT, ANZAHL, PREIS) VALUES (:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)';
    zqryMain.params.parseSQL(zqryMain.sql.text, True);
for i:=0 to self.count-1 do
begin
  if CustomerListe[i].ID=(-1) then
  zqryMain.SQL.text:='INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)' else
  zqryMain.SQL.Text:='UPDATE WARENVERKAUF1 SET KDNR=:KNR, NAME =:NAM, VORNAME=:VNA, FIRMA=:FIR, PRODUKT=:PRO, ANZAHL=:ANZ, PREIS=:PRE WHERE ID=:ID';

  zqryMain.params.ParamValues['KNR']:=self[i].KDNR;
  zqryMain.params.paramValues['NAM']:=self[i].Name;
  zqryMain.params.paramValues['VNA']:=self[i].Vorname;
  zqryMain.params.ParamValues['FIR']:=self[i].Firma;
  zqryMain.Params.ParamValues['PRO']:=self[i].Produkt;
  zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl;
  zqryMain.params.paramValues['PRE']:=self[i].Preis;
  zqryMain.ExecSQL;
  zqryMain.Next;
end;
finally
  zqryMain.free;
end;
end;
Ist das bis hierher exakt?
Ergebnis ist jedoch weiterhin, bei löschen und bearbeiten kommt "Argument außerhalb des Bereiches".
Nachdem ich LoadFromDB aufgerufen habe kann ich einen Datensatz bearbeiten, jedoch nach OK verschiebt sich die Listview-Ansicht nach links dh. Die Anzeige für die ID wird mit der KDNR und die der KDNR mit Name... belegt. Also alles wie gehabt.

DeddyH 17. Apr 2017 15:43

AW: Kundenliste
 
Beim Ändern von Daten ist das Next wieder überflüssig, welcher nächste Datensatz sollte das auch sein?

EdAdvokat 17. Apr 2017 16:22

AW: Kundenliste
 
ok zqryMain.Next; ist raus. Sicher ist hier auch einiges doppelt und könnte wohl raus:
Delphi-Quellcode:
zqryMain.sql.text:='INSERT INTO WARENVERKAUF1 (KDNR, NAME, VORNAME, FIRMA, PRODUKT, ANZAHL, PREIS) VALUES (:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)';
ist das so OK?
Es bleibt jedoch alles beim alten. "Argument außerhalb des Bereiches" und Verschiebung.

DeddyH 17. Apr 2017 17:00

AW: Kundenliste
 
Wo soll denn diese ominöse CustomerListe plötzlich herkommen? Du bist doch bereits in der TCustomerlist, wozu dann die Variable, die nirgends initialisiert bzw. instanziert wird?

EdAdvokat 17. Apr 2017 17:31

AW: Kundenliste
 
Danke habe es jetzt so gelöst:
Delphi-Quellcode:
procedure TCustomerList.SavetoDB(con: TZConnection);
var
  zqryMain: TZQuery;
  i: Integer;
begin
  zqryMain:=TZQuery.Create(nil);
  try
    zqryMain.connection:=con;
   // zqryMain.sql.text:='INSERT INTO WARENVERKAUF1 (KDNR, NAME, VORNAME, FIRMA, PRODUKT, ANZAHL, PREIS) VALUES (:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)';
   // zqryMain.params.parseSQL(zqryMain.sql.text, True);
for i:=0 to self.count-1 do
begin
  if Self[i].ID=(-1) then
  zqryMain.SQL.text:='INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)' else
  zqryMain.SQL.Text:='UPDATE WARENVERKAUF1 SET KDNR=:KNR, NAME =:NAM, VORNAME=:VNA, FIRMA=:FIR, PRODUKT=:PRO, ANZAHL=:ANZ, PREIS=:PRE WHERE ID=:ID';
  zqryMain.params.parseSQL(zqryMain.sql.text, True);

  zqryMain.params.ParamValues['KNR']:=self[i].KDNR;
  zqryMain.params.paramValues['NAM']:=self[i].Name;
  zqryMain.params.paramValues['VNA']:=self[i].Vorname;
  zqryMain.params.ParamValues['FIR']:=self[i].Firma;
  zqryMain.Params.ParamValues['PRO']:=self[i].Produkt;
  zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl;
  zqryMain.params.paramValues['PRE']:=self[i].Preis;
  zqryMain.ExecSQL;
 
end;
finally
  zqryMain.free;
end;
end;
hoffentlich ist das nicht wieder Blödsinn. Doch es bleibt alles wie es war.

DeddyH 17. Apr 2017 18:02

AW: Kundenliste
 
Zähl doch mal die SQL-Parameter.

EdAdvokat 17. Apr 2017 18:17

AW: Kundenliste
 
ich zähle 7 KDNR,Name,Vorname,Firma,Produkt,Anzahl,Preis.
Was ist da falsch?

DeddyH 17. Apr 2017 19:02

AW: Kundenliste
 
Ich zähle einmal 7 und einmal 8. Das ist schonmal ungünstig, wenn man beide Fälle gleich behandelt.

EdAdvokat 17. Apr 2017 19:07

AW: Kundenliste
 
Wo 8?

Hobbycoder 17. Apr 2017 19:17

AW: Kundenliste
 
Beim Update ist ja noch eine where Klausel dabei, die ja auch einen Parameter hat. Das musst du natürlich auch über ein if berücksichtigen.
Deswegen sind es einmal 7 und einmal 8. im insert wird die ID ja nicht benötigt.

EdAdvokat 17. Apr 2017 20:04

AW: Kundenliste
 
Ich verzweifele fast: Hoffentlich ist das nicht großer Blödsinn:
Delphi-Quellcode:
procedure TCustomerList.SavetoDB(con: TZConnection);
var
  zqryMain: TZQuery;
  i: Integer;
begin
  zqryMain:=TZQuery.Create(nil);
  try
    zqryMain.connection:=con;

for I := 0 to Self.Count-1 do
begin
  if self[i].ID=-1 then
  zqryMain.SQL.Text:='UPDATE WARENVERKAUF1 SET KDNR=:KNR, NAME =:NAM, VORNAME=:VNA, FIRMA=:FIR, PRODUKT=:PRO, ANZAHL=:ANZ, PREIS=:PRE WHERE ID=:ID';
  zqryMain.Params.ParamValues['ID']:=self[i].ID;
  zqryMain.params.ParamValues['KNR']:=self[i].KDNR;
  zqryMain.params.paramValues['NAM']:=self[i].Name;
  zqryMain.params.paramValues['VNA']:=self[i].Vorname;
  zqryMain.params.ParamValues['FIR']:=self[i].Firma;
  zqryMain.Params.ParamValues['PRO']:=self[i].Produkt;
  zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl;
  zqryMain.params.paramValues['PRE']:=self[i].Preis;
  zqryMain.ExecSQL;
  zqryMain.params.parseSQL(zqryMain.sql.text, True);
end;
for I := 0 to Self.Count-1 do
begin
  zqryMain.SQL.text:='INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)';
  zqryMain.params.ParamValues['KNR']:=self[i].KDNR;
  zqryMain.params.paramValues['NAM']:=self[i].Name;
  zqryMain.params.paramValues['VNA']:=self[i].Vorname;
  zqryMain.params.ParamValues['FIR']:=self[i].Firma;
  zqryMain.Params.ParamValues['PRO']:=self[i].Produkt;
  zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl;
  zqryMain.params.paramValues['PRE']:=self[i].Preis;
  zqryMain.ExecSQL;
  zqryMain.params.parseSQL(zqryMain.sql.text, True);
end;

finally
  zqryMain.free;
end;
end;
Ich bin wirklich nicht sicher, ob ich das wirklich packe. Ich sitze nun schon Tage über vermutlich ein einfaches Problem.
Es ist doch wohl eine Nummer zu groß für mich.Auch mit dieser Lösung bleibt alles, wie es war.

scrat1979 17. Apr 2017 22:08

AW: Kundenliste
 
Das sieht doch soooo schlecht gar nicht aus :) Du scheinst aber noch einen Syntaxfehler drin zu haben wenn ich es richtig überflogen habe.

Zunächst reicht eine FOR-Schleife aus. Und bei der IF-Abfrage fehlt ein BEGIN...END. Pseudocode:

Delphi-Quellcode:
FOR [...] DO BEGIN
 IF ID=-1 THEN BEGIN
  { INSERT }
 END ELSE BEGIN
  { UPDATE }
 END; {else}
END; {for}

Hobbycoder 18. Apr 2017 07:14

AW: Kundenliste
 
alles schon gar nicht schlecht.

Aber es müssen lediglich die SQL-Statements in den If-Block und das Bestücken von ID.
Außer dem, das parseSQL muss vor dem Zuweisen von Werten passieren. Danach macht's keinen Sinn mehr, bzw. wirkt sich nur auf den nächsten Schleifendurchlauf aus.

In etwa so:
Delphi-Quellcode:
procedure TCustomerList.SavetoDB(con: TZConnection);
var
   zqryMain: TZQuery;
   i: Integer;
begin
   zqryMain:=TZQuery.Create(nil);
   try
     zqryMain.connection:=con;

for I := 0 to Self.Count-1 do
begin
   if self[i].ID=-1 then
     zqryMain.SQL.Text:='UPDATE WARENVERKAUF1 SET KDNR=:KNR, NAME =:NAM, VORNAME=:VNA, FIRMA=:FIR, PRODUKT=:PRO, ANZAHL=:ANZ, PREIS=:PRE WHERE ID=:ID' else
     zqryMain.SQL.text:='INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)';
   zqryMain.params.parseSQL(zqryMain.sql.text, True);
   if self[i].ID=-1 then
     zqryMain.Params.ParamValues['ID']:=self[i].ID;
   zqryMain.params.ParamValues['KNR']:=self[i].KDNR;
   zqryMain.params.paramValues['NAM']:=self[i].Name;
   zqryMain.params.paramValues['VNA']:=self[i].Vorname;
   zqryMain.params.ParamValues['FIR']:=self[i].Firma;
   zqryMain.Params.ParamValues['PRO']:=self[i].Produkt;
   zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl;
   zqryMain.params.paramValues['PRE']:=self[i].Preis;
   zqryMain.ExecSQL;
end;

finally
   zqryMain.free;
end;
end;

haentschman 18. Apr 2017 07:53

AW: Kundenliste
 
Moin...:P
:warn: Warum benutzt du nicht die Anweisungen (SQL, ParamByName) die du in früheren Threads gelernt hast? http://www.delphipraxis.net/192351-g...-eingeben.html
Mit dem Debugger würdest du selber sehen warum manche Sachen nicht aufgerufen werden oder die Werte nicht stimmen (Breakpoints). :? Ohne Debugger ist programmieren wie den Ast eines Baumes abschneiden mit einem glatten Messer. :kotz: Damit solltest du dich VOR den Objektlisten beschäftigen.
Delphi-Quellcode:
procedure TCustomerList.SavetoDB(con: TZConnection);
var
  zqryMain: TZQuery;
  I: Integer;
begin
  zqryMain := TZQuery.Create(nil);
  try
    zqryMain.connection := con; // Connection zuordnen

    for I := 0 to Self.Count - 1 do // Liste durchlaufen
    begin
      if Self[I].ID = -1 then // !!!! Das hatten wir doch schonmal ähnlich...Ist die ID jedes Objektes in der Liste > -1 ????
      begin // !!! begin-end Blöcke da du erkennst was zueinander gehört...auch bei einer Anweisung macht es Sinn.
        zqryMain.SQL.Text := 'UPDATE WARENVERKAUF1 SET KDNR = :KNR, NAME = :NAM, VORNAME =: VNA, FIRMA = :FIR, PRODUKT = :PRO, ANZAHL = :ANZ, PREIS = :PRE WHERE ID = :ID'
        // Parameter nur mit ID -1 zuordnen !!! 
        zqryMain.ParamByName('ID').AsInteger := Self[I].ID;
        // As... übergibt gleich den "richtigen" Typ...Value ist was für Weicheier :-), die nicht wissen welcher Wert übergeben werden soll.
      end
      else
      begin
        zqryMain.SQL.Text := 'INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)';
        // keine Codevervollständigung benutzt wegen Schreibfehler (text). :-( 
      end;
        // zqryMain.params.parseSQL(zqryMain.sql.text, True);
        // Quatsch. ??? Wo hast du das her? Ich kenne kein Beispiel wo man das parseSQL benötigt...nur im Notfalle ist es "vorhanden".
      // Parameter zuordnen
      zqryMain.ParamByName('KNR').AsString := Self[I].KDNR;
      zqryMain.ParamByName('NAM').AsString := Self[I].Name;
      zqryMain.ParamByName('VNA').AsString := Self[I].Vorname;
      zqryMain.ParamByName('FIR').AsString := Self[I].Firma;
      zqryMain.ParamByName('PRO').AsString := Self[I].Produkt;
      zqryMain.ParamByName('ANZ').AsInteger := Self[I].Anzahl;
      zqryMain.ParamByName('PRE').AsString := Self[I].Preis; // besser Typ Float
      // As... übergibt gleich den "richtigen" Typ...Value ist was für Weicheier :-), die nicht wissen welcher Wert übergeben werden soll.
      // Ausführen
      zqryMain.ExecSQL;
    end;
  finally
    zqryMain.Free; // keine Codevervollständigung benutzt wegen Schreibfehler (free). :-( 
  end;
end;

EdAdvokat 18. Apr 2017 08:28

AW: Kundenliste
 
vielen Dank. Habe nun die else-Bedingung auch eingefügt und so sieht nun die SaveToDB-Methode aus:
Delphi-Quellcode:
procedure TCustomerList.SavetoDB(con: TZConnection);
var
  zqryMain: TZQuery;
  i: Integer;
begin
  zqryMain:=TZQuery.Create(nil);
  try
    zqryMain.connection:=con;
 

for I := 0 to Self.Count-1 do
begin
   if self[i].ID=-1 then
     zqryMain.SQL.Text:='UPDATE WARENVERKAUF1 SET KDNR=:KNR, NAME =:NAM, VORNAME=:VNA, FIRMA=:FIR, PRODUKT=:PRO, ANZAHL=:ANZ, PREIS=:PRE WHERE ID=:ID' else
     zqryMain.SQL.text:='INSERT INTO WARENVERKAUF1(KDNR,NAME,VORNAME,FIRMA,PRODUKT,ANZAHL,PREIS) VALUES(:KNR, :NAM, :VNA, :FIR, :PRO, :ANZ, :PRE)';
     zqryMain.params.parseSQL(zqryMain.sql.text, True);
   if self[i].ID=-1 then
   begin
   zqryMain.Params.ParamValues['ID']:=self[i].ID;
   zqryMain.params.ParamValues['KNR']:=self[i].KDNR;
   zqryMain.params.paramValues['NAM']:=self[i].Name;
   zqryMain.params.paramValues['VNA']:=self[i].Vorname;
   zqryMain.params.ParamValues['FIR']:=self[i].Firma;
   zqryMain.Params.ParamValues['PRO']:=self[i].Produkt;
   zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl;
   zqryMain.params.paramValues['PRE']:=self[i].Preis;
   zqryMain.ExecSQL;
   end else
   begin
      zqryMain.params.ParamValues['KNR']:=self[i].KDNR;
      zqryMain.params.paramValues['NAM']:=self[i].Name;
      zqryMain.params.paramValues['VNA']:=self[i].Vorname;
      zqryMain.params.ParamValues['FIR']:=self[i].Firma;
      zqryMain.Params.ParamValues['PRO']:=self[i].Produkt;
      zqryMain.Params.ParamValues['ANZ']:=self[i].Anzahl;
      zqryMain.params.paramValues['PRE']:=self[i].Preis;
      zqryMain.ExecSQL;
   end;
end;

finally
  zqryMain.free;
end;
end;
ich hoffe, dass es nun so richtig ist. Leider bleibt alles beim alten, denn weiterhin kommt nach dem Programmaufruf und dem Versuch einen Datensatz zu löschen oder zu bearbeiten die Meldung "Argument außerhalb des Bereiches" und nach dem Aufruf von LoadFromDB und bearbeiten verschieben sich die Spalten nach links (Name steht unter KDNR Vorname unter Name usw) Da muss doch irgendwo ein Fehler mit der Spaltenzuordnung sein, den ich einfach nicht finden kann.Wenn sich die ganze Sache verschoben hat, kann ich jedoch den Datensatz bearbeiten und auch löschen. Im Listview ist dann nur ID nicht mehr sichtbar und die Tabelle ist wie erwähnt nach links gerückt.
Bei der Suche drehe ich mich im Kreis. Ich danke Euch jedoch für Eure Geduld mit mir. Ich würde es gern doch noch packen, um dann Zeile für Zeile alles nochmals durchzugehen und daraus zu lernen. Das ist überhaupt der tiefe Sinn des ganzen.

DeddyH 18. Apr 2017 08:29

AW: Kundenliste
 
Wo genau kommt denn der Fehler? Bemüh doch mal den Debugger, dafür ist er ja da.

Jumpy 18. Apr 2017 09:56

AW: Kundenliste
 
Müsstest du nicht nach LoadFromDB usw. auch wieder FuelleListView aufrufen. Irgendwie bin ich mir nicht sicher ob CustomerList und ListView immer sauber synchron sind. Ausserdem verstehe ich nicht, warum du die Cutomerlist einmal in der Connect-Funktion von außen füllst und dann wieder mit der LoadFromDB-Methode von innern füllst, da reicht doch eine Version.

Dann hast du öfter solche Konstrukte:
Delphi-Quellcode:
   if lvCustomer.Selected<>nil then
  begin
    //Über den Index kann man nun auf jedes Object in der ObjektListe zugreifen
    //Damit bestücken wir das Formular mit den Werten aus den Eigenschaften des Objects
    frmCustomer.edtKDNR.Text:=inttostr(CustomerList[lvCustomer.Selected.Index].KDNR);
    frmCustomer.edtName.Text:=CustomerList[lvCustomer.Selected.Index].Name;
    frmCustomer.edtVorname.Text:=CustomerList[lvCustomer.Selected.Index].Vorname;
    //...
    if FRMCustomer.ShowModal=mrOK then
    begin
      //Object erzeugen nicht nötig, da es bereits besteht. Zugriff also möglich
      //für die Übertragung der Daten aus dem Formular
      CustomerList[lvCustomer.Selected.Index].KDNR:=strtoint(frmCustomer.edtKDNR.Text);
      CustomerList[lvCustomer.Selected.Index].Name:=frmCustomer.edtName.Text;
    //...
Das wäre mir alles viel zu lang zum lesen, da würde ich mit Hilfsvariablen arbeiten und dem frmCustomer ein paar Propertys spendieren, die das füllen der ganzen TEdits intern erledigen:

Delphi-Quellcode:
var index:integer;
    customer:TCustomer;
begin
  if lvCustomer.Selected<>nil then
    begin
    index:=lvCustomer.Selected.Index;
    customer:=CustomerList[index];
    frmCustomer.KDNR:=customer.KDNR;
    frmCustomer.Nachname:=Customer.Name;
    frmCustomer.Vorname:=Customer.Vorname;
    //...
    if FRMCustomer.ShowModal=mrOK then
    begin
      //Object erzeugen nicht nötig, da es bereits besteht. Zugriff also möglich
      //für die Übertragung der Daten aus dem Formular
      Customer.KDNR:=frmCustomer.KDNR;
      Customer.Name:=frmCustomer.Nachname;
    //...
Läßt sich viel schöner lesen, denke ich. Alternativ könnte man dem frmCustomer auch einfach einen kompletten TCustomer übergeben und der regelt dann alles intern selbst, dann säh das nur noch so aus (man müsste dann aber das frmCustomer aufwendiger gestalten:

Delphi-Quellcode:
var index:integer;
begin
  if lvCustomer.Selected<>nil then
    begin
    index:=lvCustomer.Selected.Index;
    frmCustomer.customer:=CustomerList[index];
    if FRMCustomer.ShowModal=mrOK then
      FuelleListView;
    end;
end;

p80286 18. Apr 2017 11:01

AW: Kundenliste
 
Zitat:

Zitat von EdAdvokat (Beitrag 1368021)
Leider bleibt alles beim alten, denn weiterhin kommt nach dem Programmaufruf und dem Versuch einen Datensatz zu löschen oder zu bearbeiten die Meldung "Argument außerhalb des Bereiches"

Diese Meldung fällt nicht vom Himmel, sondern wird von Deinem Programm Irgendwo erzeugt.
Also nutze bitte den Debugger um die Stelle zu finden, die diese Meldung erzeugt!

Zitat:

Zitat von EdAdvokat (Beitrag 1368021)
und nach dem Aufruf von LoadFromDB und bearbeiten verschieben sich die Spalten nach links (Name steht unter KDNR Vorname unter Name usw) Da muss doch irgendwo ein Fehler mit der Spaltenzuordnung sein, den ich einfach nicht finden kann.Wenn sich die ganze Sache verschoben hat, kann ich jedoch den Datensatz bearbeiten und auch löschen. Im Listview ist dann nur ID nicht mehr sichtbar und die Tabelle ist wie erwähnt nach links gerückt.

da könnte man vermuten, daß die erste Spalte gar nicht mehr vorhanden ist, bzw die Inhalte der zweiten in der ersten, die der dritten in der zweiten usw. stehen.
Wenn Du an genau einem Ort (Procedure/Funktion) Deine Liste Befüllst, dann hast Du auch den Überblick was da geschieht.

Zitat:

Zitat von EdAdvokat (Beitrag 1368021)
Bei der Suche drehe ich mich im Kreis.

darum sollt Du ja dein Programm strukturieren, damit klar ist, wenn wenn ein Update fehlschlägt, daß der Fehler in der Datenbankroutinen zu finden ist, und nicht irgendwo zwischen der Routinen für die GUI.

Gruß
K-H


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:44 Uhr.
Seite 1 von 3  1 23      

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