Lav Delphi Komponent

Tags:    delphi
Skrevet af Bruger #58 @ 16.06.2001
Komponenter i Delphi - 1. del

I denne artikelserie vil jeg beskrive hvordan man kan lave sine egne komponenter i Delphi. Først vil jeg dog give en lille forklaring på hvad man normalt forstår ved et komponent (også kaldet teori :-), og derefter gå i gang med et rigtigt komponent.

Hvad er et komponent

Et komponent kan nok bedst beskrives som en klasse der kan fremstå som et visuelt objekt i Form Designeren. Det betyder altså at et komponent bliver vist som et ikon på komponent-paletten, der så kan lægges på en form. Komponenter kommer i tre varianter: Usynlige (f.eks. en TOpenDialog), grafiske (f.eks. en label) og komponenter med et vindue (f.eks. en edit-boks).

Du kan eventuelt nu springe ned til den næste overskrift hvis du ikke er interesseret i resten af teorien! :-) For at forklare det lidt nærmere, så er et usynligt komponent normalt repræsenteret som et ikon på formen, som man så kan vælge og redigere egenskaberne for med Object Inspector. Når man kører programmet så kan man ikke se komponenten, med mindre komponenten opretter en eller anden form for vindue selv (f.eks. en TOpenDialog). Faktisk kunne et usynligt komponent sagtens undværes og erstattes med et objekt man så kan Create i run-time, men det er normalt nemmere at bruge et usynligt komponent.

Forskellen på et grafisk komponent og et komponent med vindue, er at et grafisk komponent ikke har sit eget Window Handle, mens et komponent med vindue har. Og hvad forskel gør det så? Jo, et Window Handle optager nogen system ressourcer og gør normalt også komponenten langsommere, til gengæld så er det faktisk også den eneste måde man kan få Windows til at sende messages (events) til komponenten! Det kan så måske lyde lidt underligt, at når en label ikke har et Window Handle, hvordan kan man så give en f.eks. en OnClick event? Der skal så lige siges at et grafisk komponent på en måde alligevel har er Window Handle, det deler nemlig handle med det komponent det er placeret på (parent)! Derfor kan Delphi fange Click messages der bliver sendt til eks. formen og så tjekke om de skulle ligge inden for et område hvor der ligger en label. Hvis klikket så gør det, så bliver eventen bare sendt videre til labelen.

Et simpelt komponent

Men nu hastigt videre til et egentligt eksempel: Et komponent der kan konvertere mellem forskellige datatyper! (wohoo! :-) Det skal være et usynligt komponent med egenskaber for String, Integer og Real værdier. Når man ændrer værdien af en af egenskaberne bliver de to andre også ændret for at reflektere den nye værdi. Hvis man sætter værdien af String og den nye værdi ikke kan konverteres til et tal, så kommer der bare til at stå 0 i de to andre egenskaber. Hvis man skriver et tal som f.eks. 1,37 ind i Real egenskaben bliver det selvfølgelig konverteret til en String og i Integer feltet kommer der til at stå den afrundede værdi af tallet.

Først skal du selvfølgelig (surprise) have Delphi kørende. Det vil være en god ide at lukke et evt. projekt du skulle have åbent, så bliver det nemmere at arbejde, så: File -> New -> Application. Når du har et helt frisk projekt, så vælger du Component menuen og New Component.
TTypeConverter komponentens egenskaber i Object Inspector

Nu kommer allerede den første (mere eller mindre) svære beslutning. Du skal nemlig vælge hvilken klasse dit nye komponent skal nedarve fra. I dette tilfælde skal det være TComponent det nedarver fra, det er nemlig det alle usynlige komponenter nedarver fra. Så i den øverste boks Ancestor vælger du TComponent.

Nu skal vi finde ud af hvad komponenten skal hedde, lad os kalde den for TTypeConverter, så i den næste boks Class Name skriver du altså TTypeConverter. I Palette Page feltet skal du vælge hvilken side på komponent-paletten din nye komponent skal placeres på. Du kan enten vælge en af dem der allerede findes, eller du kan skrive navnet på en ny side som så automatisk vil blive lavet. I Unit File Name feltet skal du skrive filnavnet for den nye komponent. Search Path kan du bare lade være som den er. Klik Ok.

Kodning af TTypeConverter

Nu laver Delphi en ny unit med en klasse-deklaration og en Register procedure. Det er her du skal skrive koden til dit komponent. Det første vi skal gøre er at lave nogen felt-variabler for de forskellige egenskaber. Det gør man i private sektionen, så der skal du skrive:

type
  TTypeConverter = class(TComponent)
  private
    { Private declarations }
    FStringVal: string;
    FIntVal: integer;
    FRealVal: Extended;
Læg mærke til at jeg har skrevet et F foran alle navnene. Det har jeg gjort af to grunde: For det første er det god praksis altid at kalde felt-variabler noget med et F først, og for det andet er det for at kunne kende dem fra egenskaber.

Nu skal vi have deklareret nogen procedurer der bliver kaldt når man ændrer en egenskab. De skal også stå i private sektionen. Tilføj dette til private:
type
  TTypeConverter = class(TComponent)
  private
    { Private declarations }
    . . .
    procedure SetStringVal(Value: string);
    procedure SetIntVal(Value: integer);
    procedure SetRealVal(Value: Extended);
Nu mangler vi kun de egentlig egenskaber. De skal stå i published sektionen for at man kan se dem i Object Inspector (Delphi genererer kun Run Time Type Information for properties der er Published). En egenskab skal altid deklareres med enten en read metode, en write metode eller begge dele. Hvis man kun deklarerer en Read metode bliver egenskaben read-only. Hvis man kun deklarerer en Write metode bliver egenskaben write-only (hvad man så end skal bruge det til). Men til noget kode:
type
  TTypeConverter = class(TComponent)
  . . .
  published
    { Published declarations }
    property StringVal: string
     read  FStringVal
     write SetStringVal;
    property IntVal: integer
     read  FIntVal
     write SetIntVal;
    property RealVal: Extended
     read  FRealVal
     write SetRealVal;
  end;
protected og public sektionerne skal vi ikke bruge, så dem kan vi sagtens slette. Vi skal også have skrevet nogen procedurer til det vi har deklareret. Hvis du har Delphi 4 eller nyerer, så kan du trykke Ctrl+Shift+C for at aktivere Class Completion. Den finder automatisk alle metoder og egenskaber i alle klasser i den unit man er i, og generer den kode der minimum skal til for at man kan kompilere. Hvis du har Delphi 3 eller ældre så bliver du nødt til selv at skrive den nødvendige kode. Her er et skelet:
{ TTypeConverter }

procedure TTypeConverter.SetStringVal(Value: string);
begin

end;

procedure TTypeConverter.SetRealVal(Value: Extended);
begin

end;

procedure TTypeConverter.SetIntVal(Value: integer);
begin

end;
Det skal stå i implementation delen af unit'en, under Register proceduren. Nu kan vi så endelig fylde noget kode på. I hver af procedurerne skal der stå kode til at tildele Value parameteren til den tilhørende felt-variabel. Desuden skal Value parameteren konverteres til de to andre typer. Nu giver jeg koden for SetStringVal proceduren (som er den mest tricky) og så kan du sikkert selv regne resten ud.
procedure TTypeConverter.SetStringVal(Value: string);
begin
  // Hvis værdien ikke har ændret sig så er der ingen grund til at
  // køre resten af koden.
  if FStringVal <> Value then
  begin
    // Den første er helt ligetil
    FStringVal := Value;
    // Hvis det ikke lykkes at konvertere til en Real-værdi, så tildel
    // i stedet en nul-værdi.
    try
      FRealVal := StrToFloat(Value);
    except
      FRealVal := 0;
    end;
    // Siden vi allerede har gemt en værdi i FRealVal så kan vi bare
    // tage den og afrunde den for at få fat i integer-værdien. Hvis
    // vi ikke brugte en afrundet værdi for FIntVal så ville den altid
    // blive nul for decimal-værdier.
    FIntVal := Round(FRealVal);
  end;
end;
Nu er vores komponent næsten færdigt. Faktisk mangler der kun et ikon til det! Til det skal vi bruge Borland's Image Editor, den kan man normalt starte fra Tools menuen i Delphi. I Image Editor skal du vælge File -> New -> Component Resource File. Nu skal du så vælge Ressource -> New -> Bitmap og lave et nyt 24 x 24 bitmap i 16 farver. (Det er den størrelse Delphi bruger.) Dobbeltklik på det nye Bitmap1 punkt der er kommet for at komme ind i editoren. Maksimer evt. vinduet, og bruge så Ctrl+I for at zoome ind.
TTypeConverter komponentens egenskaber i Object Inspector

Nu kan du tegne et ikon til komponenten, det behøver ikke være den store kunst, men det er nu en god ide at man får en ide om hvad komponenten gør ud fra ikonet. Jeg har lavet et ikon hvor der øverst står ABC og neden under står 123, og så er der pile mellem dem i begge retninger. Luk nu vinduet med bitmappen men ikke Image Editor! Når du er tilbage i Image Editor, så klik en enkelt gang på navnet Bitmap1 (eller højreklik og vælg Rename) og omdøb ikonet til TTYPECONVERTER (altså komponentens navn med rent store bogstaver).
TTypeConverter komponentens egenskaber i Object Inspector

Gem nu filen samme sted som uniten med komponenten. Den skal hedde det samme som uniten, så du skulle nu have TypeConverter.pas og TypeConverter.dcr i samme mappe. Afslut Image Editor. Du skulle nu kunne kompilere komponenten. Vælg Component -> Install Component i Delphi. Nu kan du enten vælge at installere komponenten i en allerede eksisterende pakke, eller at lave en ny pakke. (Hvis du bruger Delphi 1 eller 2 så bliver du ikke spurgt om det, men hvis du bruger Delphi 1 eller 2 så burde du alligevel opgradere.) Det kan være en meget god ide at lave en ny pakke, så vælg Into New Package og find selv på et filnavn til pakken og en beskrivelse af den. Det kunne f.eks. være "Mine egne komponenter".
TTypeConverter komponentens egenskaber i Object Inspector

Klik nu på Ok. Du vil sandsynligvis blive spurgt om: "Package (indsæt navn her) will be rebuilt. Continiue?". Der skal du bare vælge Yes. Delphi kompilerer nu pakken med dit nye komponent, den burde ikke give nogen fejl. Hvis den gør, så spørg i forummet. :-) Bagefter skulle den vise: "... The following new component(s) have been registered: TTypeConverter". Prøv at se på komponent-paletten om ikke dit nye komponent skulle være på den side du valgte i starten. Åbn formen i projektet (Shift+F12) og placer komponenten på den. Prøv at lege lidt med at skrive forskellige værdier for StringVal, IntVal og RealVal.
TTypeConverter komponentens egenskaber i Object Inspector


Tillykke!

Du skulle nu have dit første (med mindre du har lavet komponenter før :-) fungerende komponent! Husk at hvis der er noget der driller, så kan du altid spørge i foraerne her på Udvikleren. Og der vil selvfølgelig komme flere artikler om hvordan man laver komponenter! Du kan hente kildekoden til det komponent jeg har beskrevet i artiklen fra Downloads sektionen her på Udvikleren.


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (2)

User
Bruger #1330 @ 02.02.04 15:27
Dejlgit med en artikel om hvordan man koder kompontener, det gør det sgu en del nemmere at kode større apps :D
User
Bruger #4734 @ 10.05.04 21:21
Hmm... Ved sku ikke lige hvad der går galt..
Her på det sidste har jeg ikke kunnet få downloadede komponenter til at virke.. Og hjemmelavede er det ikke bedre med... Jeg fik ikke mindre end 18 fejl... FX. Undeclared identifier "TTypeConverter"... Nogen der ved hvad der går galt?
Du skal være logget ind for at skrive en kommentar.
t