• Nieuwe artikelen

  • Business Objects - het begin.

    Werken met DataCoponenten TTable, TDataSource, en de daaraan gekoppelde TDBEdit TDBGrid en de hele familie, is geweldig in Delphi. Nergens heb ik met zoÔÇÖn gemak met databases gewerkt.
    Allemaal mooi, tot de dag dat je graag wil afwijken van die standaarden, en je ÔÇÿeigenÔÇÖ manier wil opleggen. Wat kun je doen?

    Hier al een begin van Business Objects, en hoe je die in jouw geval kunt gebruiken.


    Delphi heeft het grandioze voordeel dat je er in RAD-mode (Rapid Application Development) kunt werken. Eigenlijk werken de meesten van ons zo. Snel componenten droppen op forms, wat code in button-events steken, en je applicatie werkt. Dit werkt heel goed. Het is een plezier om zo met Delphi te werken.
    Wens je af te wijken van de standaard manier waarop de componenten werken, dan dien je andere componenten te zoeken, kopen of je moet zélf klassen of componenten maken.

    Eén van de belangrijke redenen om zelf klassen te schrijven, is dat je hetgeen in je database zit, niet eenvoudigweg via zogenaamde ÔÇÿdata-awareÔÇÖ componenten op je scherm te toveren is. Je wil je data omvormen, je wil je data tussentijds in je applicatie gebruiken, je dient berekeningen en controleÔÇÖs uit te voeren. Of omgekeerd, je wil je editvelden ook voor andere dingen gebruiken dan voor het afbeelden van data: het zoeken van data bijvoorbeeld.

    Een eerste manier om in te grijpen volgt hier.

    Je kunt een aantal omvormingen en ÔÇÿtruukenÔÇÖ toepassen in de OnGetText en OnSetText-events van fields.Wil je het klantnummer bijvoorbeeld afbeelden in een TPanel, dan selecteer je eerst het TField waarin het klantnummer staat, en dan zeg je in zijn OnGetText dat je de waarde in Panel1 zet.

    • Klik met je rechter muisknop op je TTable-component, en je krijgt een context-menu.
    • Selecteer Fields Editor
    • Klik met de rechter muisknop in het witte vak, en selecteer All Fields
    • Nu zie je alle velden van je TTable.
    • Klik op het veld waarin je klantnummer staat (vb. KlantNummer)
    • In de Object Inspector zie je nu een TNumberField voor je klantnummer.
    • Maak een handler voor het OnGetText-event, door het tabblad Events te kiezen, en naast OnGetText te dubbelklikken.

    In deze handler zeg je wat je met de waarde van het veld gaat doen als het TField vanuit de database opgevuld wordt:


    Code:
    procedure TFormF.MijnFieldGetText(Sender: TField; var Text: string;  DisplayText: Boolean);
    begin  
      Panel1.Caption := sender.asstring
    end ;
    Zet de property visible van het TEditField dat met het field KlantNummer verbonden is, op false. Je ziet de TDBEdit (in runtime) niet meer, maar wel je TPanel, waar je klantnummer op afgebeeld wordt.

    Op deze manier beeld je je gegevens anders af dan gewoon rechtstreeks in TDBEdits.
    Je kunt natuurlijk de waarden in een TEdit afbeelden in plaats van in een TPanel, zoals hierboven.

    Omgekeerd kun je hetgeen je in TEdits steekt, copiëren naar TDBEdits.

    In de OnExit-event copieer je hetgeen ingetikt hebt, naar de overeekomstige TDBEdit. Dus je maakt een TEdit voor je klantnummer, en in de OnExit schrijf je het volgende:

    Code:
    procedure TForm1.Edit1Exit(Sender: TObject);
    begin
    Table1Klantnummer.AsString := (Sender as TEdit).Text
    end;
    Dien je nog controles uit te voeren of een klantnummer geldig is, dan doe je dit in de OnExit:

    Code:
    if StrToInt ((Sender as TEdit).Text) > 0 then
    Table1Klantnummer.AsString := (Sender as TEdit).Text
    Gebruik je nu dit editveld om te zoeken als je in een ÔÇÿZoekmodeÔÇÖ staat, dan zet je dit ook in de OnExit:

    Code:
    if not InZoekMode then
     if StrToInt ((Sender as TEdit).Text) > 0 then    
       Table1Klantnummer.AsString := (Sender as TEdit).Text
    In de zoekbutton schrijf je dan de code die een query maakt met hetgeen in het editveld is ingetikt, bijvoorbeeld:

    Code:
      Query1.Params[0].AsString := (Sender as TEdit).Text ;
      Query1.ExecSQL ;
    Hoewel deze methode werkt, is dit om twee redenen geen ideale oplossing. Ten eerste moet je een omweg maken via onzichtbare TDBEdits. Niet echt een ÔÇÿpropereÔÇÖ manier van werken. Ten tweede staat jouw kennis over een klantnummer in het OnExit-event van een TEdit. Wat als je een klantnummer nog elders wil gebruiken? Terug diezelfde code die in het OnExit-event staat, copiëren? Of simpelweg vergeten?

    Als we nu eens alle data en logica van een klant eens in een klasse zouden zetten? Is dit niet de bedoeling van klassen? Dan maak je in die klasse alle ÔÇÿlogicaÔÇÖ die je kent van een klant. Verder maak je een method om een klant te zoeken, en één om een klant weg te schrijven, en je hebt je eerste ÔÇ£Business ObjectÔÇØ. Je mag van die klant een component maken, maar dat hoeft helemaal niet. Je klasse zou er zo kunnen uitzien:

    Code:
    unit KlantUnit ;
     
    interface
    type
    TKlant = class (TObject)
    private  
      FNummer : integer ;
      FFirmanaam: string ;
      FTable: Ttable ;
      procedure SetNummer ( value: integer ) ;
    public
      constructor Create ;
      destructor Destroy ;
      function Post : Boolean ;
      function Zoek: Boolean ;
      property Nummer : integer read FNummer write SetNummer ;
      property Firmanaam: string read FFirmanaam write FFirmanaam ;
      property Table: TTable read FTable write FTable;
    end ;
     
    implementation
     
    procedure TKlant.SetNummer ( value: integer ) ;
    begin
      if value > 0 then { hier zit mijn ÔÇ£business logicaÔÇØ; enkel en alleen hier}    
        FNummer := value
    end ;
     
    function TKlant.Post : Boolean ;
    begin
      if assigned (FTable) then
    {wellicht zul je hier wat meer testen willen toevoegen om na te gaan of je de dataset wel kunt gebruiken}
        result := FTable.InsertRecord ( [FNummer, FFirmanaam] )
    end ;
     
    function TKlant.Zoek: Boolean ;
    begin
      if assigned (FTable) then
        result := FTable.FindKey([FNummmer])
     end ;
    Door een Table te koppelen aan je klant, kan een klant zichzelf wegschrijven.

    In je pogramma kun je nu een (of meerdere) klant-object maken en gebruiken. Dit noemt men het koppelen van de GUI aan de business objecten. Bijvoorbeeld:

    Code:
    uses  KlantUnit;
    var
    SchermKlant : TKlant ;
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      SchermKlant := TKlant.Create ;
      SchermKlant.Table := Table1
    end ;
     
    procedure Form1.ZoekButtonClick (Sender: TObject);
    begin
      SchermKlant.Nummer := Edit1.Text ;
       if SchermKlant.Zoek then
         Edit2.Text := SchermKlant.FirmaNaam 
    end ;
    Je ziet dat je in je form geen rechtstreeks gebruik meer maakt van datasets, maar dat je in je form business objecten gebruikt, die op hun beurt datasets aanspreken. Dat is het hele idee van de ÔÇÿbusiness object layerÔÇÖ.
    Maar je mist iets: Alle mooie DataControls, zoals TDBEdit, TDBGrid en zo meer, kun je niet meer gebruiken. Deze link je namelijk aan een TDataSource, die op zijn beurt aan een TTable (of andere) gelinkt is. En een TDataSource kun je niet aan een TKlant linken.
    Dus, een TKlant gebruiken met je datacontrols? Vergeet het maar!

    Een andere manier om een ÔÇÿdata-awareÔÇÖ business klasse te maken, is ze rechtstreeks af te leiden (inheriten) van TTable of TDBDataSet.Bijvoorbeeld:

    Code:
    TKlant = class (TTable) ;
    
    In dit geval ga je zelf TFields maken met velden die niet ÔÇôof anders- weggeschreven worden in de database. Het voordeel is dat je de data-aware components (zoals een TDBEdit, TDBGrid, etc.) gewoon kunt blijven gebruiken door ze te linken aan je zelfgemaakte TTables.

    Ik heb dit ooit, met vallen en opstaan, maar uiteindelijk met succes gedaan. Ik zal eens diep in mijn archieven moeten zoeken om terug uit te pluizen wat je hiervoor allemaal moet doen. En dit toetsen aan de kennis die ik ondertussen bijgekregen heb. Daarna zal ik een vervolg breien op dit artikel.

    Of mocht iemand anders met dit artikel ÔÇ£Het LichtÔÇØ gezien hebben, en dit willen neerschrijven in een vervolgartikel, hou je vooral niet in!

    Sam Witse.
    This article was originally published in forum thread: Business Objects - het begin. started by SamWitse View original post
  • Nieuwste forumberichten

    rvk

    Wanneer gebeurt wat?

    Button1.Caption past ie inderdaad wel direct aan. Maar Button1.Width := 400; bijvoorbeeld weer niet
    Zo blijf je aan de gang.
    Het beste

    rvk Yesterday, 14:48 Go to last post
    Reidinga

    Components Converteren van Delphi naar Lazarus

    Dankjewel Frans! Dat was het dus - ik heb alles nagelopen, maar schijnbaar nog niet genoeg onderzoek gedaan heb hier dwars over heen gekeken. Ik heb lazarus

    Reidinga Yesterday, 14:48 Go to last post
    Duiker

    Wanneer gebeurt wat?

    Self.Label1.Caption := 'a';
    Er vind nu geen refresh plaats omdat dat pas aan het einde van de procedure gebeurd. Dit komt omdat het label nergens

    Duiker Yesterday, 14:40 Go to last post
    rvk

    Wanneer gebeurt wat?

    Ja, en alleen de refresh van de labels. Andere componenten zouden e.v. wel direct aangepast kunnen zijn.
    Als je bijvoorbeeld een Self.Memo1.Lines.Add('a')

    rvk Yesterday, 12:13 Go to last post
    EricLang

    grid scroll vreemd verschijnsel (bug)

    Yep, ik snap het probleem. Het is wel degelijk een bug in mijn ogen, want de range-select verandert VOORDAT ik de muis heb losgelaten.
    Ik ga denken

    EricLang Yesterday, 12:08 Go to last post
    Duiker

    Wanneer gebeurt wat?

    Inderdaad Miep. Dat werkt wonderbaarlijk.

    Het komt dus omdat zoals marcov zegt, dat de refresh van het scherm pas na het event komt.

    Duiker Yesterday, 10:54 Go to last post