Delphi tools: Name property editor

Erik Stok (aka Baldo)

Delphi's open tools api (OTA) biedt geweldige mogelijkheden om de IDE uit te breiden met handigheden van jezelf. Er is veel over de OTA te vinden op het web, maar veel artikelen vertellen vooral wat er kan en verzuimen een praktisch toepasbaar voorbeeld te geven van het gebruik van de OTA. NLDelphi is een plaats die draait om Delphi in de praktijk, dus een betere plaats om de praktische kan van de OTA te beschrijven kan ik me niet indenken. Dit eerste artikel beschrijft het bouwen van een property editor.

Het idee

Een van de eerste uitbreidingen die ik op Delphi maakte was een property editor om de naam van componenten snel in te kunnen vullen. Ik wil namelijk wel componenten een nette naam geven, maar niet heen en weer klikken en tweemaal gegevens intoetsen. Het viel me namelijk op dat ik bijvoorbeeld bij het plaatsen van een button op een form allereerst de caption property zette (bijvoorbeeld op 'Sluiten') waarna ik de naam van de button op praktisch hetzelfde zet (in dit voorbeeld op 'btnSluiten'). Het werken met prefix of suffix maakt natuurlijk voor het patroon van handelingen geen verschil.

Het eerste wat ik wilde doen was ervoor zorgen dat ik voor dezelfde componenten in ieder geval dezelfde prefix zou gebruiken. Het volgende doel was het gebruiken van bepaalde properties van het component om de naam samen te stellen.

Het meest gemakkelijk leek mij een dubbelklik op de naam van het component waarna de prefix zou zijn ingevuld voor het betreffende component. Een property editor lag voor de hand dus. In dit artikel zal ik de bouw van deze property editor behandelen.

De benodigdheden zijn dus: een property editor die voor het betreffende component de juiste prefix toont en een lijst met prefixen voor alle bekende componenten.

De lijst met bekende prefixen samenstellen is echter een hele klus. En als ik een nieuwe componentenbibliotheek installeer dan moet de lijst weer aangevuld worden. En wat als ik een component nooit gebruikt? Dan ga ik voor niets een prefix bedenken. De oplossing is eenvoudig. De property editor toont een combobox met één of twee opties: als eerste de prefix van het component als die bekend is en als tweede de optie om de lijst met prefixen te onderhouden. Een dubbelklik op de name property zal dan leiden tot het invullen van de prefix en als deze niet bekend is wordt automatisch het prefix onderhoud gestart.

Property editors

In de DesignEditors unit (terug te vinden in de Delphi map onder Source\ToolsAPI) zit een class genaamd TPropertyEditor. Door een afgeleide van deze class te maken en bepaalde methods te overriden is het eenvoudig een eigen property editor te maken. Als in de Delphi help gezocht wordt op TPropertyEditor dan staat vrij expliciet uitgelegd wat de TPropertyEditor allemaal kan. Ik ga hier dan ook niet verder in op de details van TPropertyEditor, behalve die details die noodzakelijk zijn om de name property editor te bouwen.

Het zal geen verassing zijn dat de class definition voor de name property editor er als volgt uitziet:

TNamePropertyEditor = class(TPropertyEditor)
protected
function FirstComponent: TComponent; virtual;
function PrefixClassName: String; virtual; function ReadPrefixFromIniFile: String;
procedure WritePrefixToIniFile(Prefix: String);
public
function GetAttributes: TPropertyAttributes; override;
function GetValue: String; override;
procedure SetValue(const Value: String); override;
procedure GetValues(Proc: TGetStrProc); override;
end;

Via de GetAttributes method kunnen we aangeven waartoe deze property editor allemaal in staat is. In dit geval is een combobox gewenst, dus het resultaat van deze method is de paValueList optie.

Bij een combobox implementatie geldt dat de GetValue method de huidige waarde teruggeeft, dat de SetValue method de nieuw geselecteerde waarde zet en dat de GetValues method een lijst van mogelijk te selecteren waarden geeft.

Bij de GetValue method zal dus altijd de waarde van de name property teruggegeven worden als resultaat.

De SetValue method zal twee dingen moeten kunnen. Enerzijds zal de geselecteerde prefix als nieuwe waarde van de name property gezet moeten kunnen worden. Anderzijds zal er bij het selecteren van de optie 'onderhoud prefixen' het scherm voor het onderhoud van de prefixen moeten worden gestart.

De GetValues method zal een lijst terug moeten geven met één of twee items. De optie 'onderhoud prefixen' en eventueel de prefix die voor dit component bekend is.

De protected methods zijn ondersteunende methods. FirstComponent helpt om eenvoudig het component te bepalen waarvan momenteel de name property wordt bewerkt. De PrefixClassName method geeft de naam van de class van het component.

Onderhoud op de lijst

De lijst van bekende prefixen is een eenvoudige lijst. Door bij te houden van elke componentclass wat de bijbehorende prefix is kan per component een prefix worden getoond. Deze lijst kan bijvoorbeeld worden opgeslagen in een .ini bestand. Het bestand kan de volgende structuur hebben:

[Prefixes]
  TButton=btn
  TPanel=pnl
  TDataSource=dts
  …

De method ReadPrefixFromIniFile leest een prefix uit het ini bestand met de bovengenoemde structuur. WritePrefixToIniFile schrijft een prefix naar hetzelfde ini bestand.

Het onderhoud op deze lijst is geïmplementeerd met behulp van een InputQuery dialog. Natuurlijk kan hiervoor een veel meer elegante oplossing gekozen worden, maar voor dit artikel is het voldoende.

Speciale componenten

Er zijn wat speciale gevallen waarin de name property editor zich anders moet gedragen. De prefix van een form is namelijk niet te bepalen, omdat de classname van het 'component' per form verschilt. Om toch voor ieder form dezelfde prefix te kunnen hanteren, wordt gekeken naar de owner van het component. Als de prefix moet worden bepaald van een component zonder owner, dan wordt veronderstelt dat dit een TForm afgeleide is.

Voor datamodules geldt hetzelfde als voor forms. Een datamodule heeft echter wél een owner. Dit is of een TDataModuleForm of een TDataModuleDesigner.

Registratie

Een essentieel onderdeel bij de bouw van een property editor is de registratie. Net als bij het bouwen van eigen componenten die in de IDE worden geregistreerd, moet ook bij property editors de Register procedure worden geïmplementeerd. En ook hier geldt dat de naam van deze procedure hoofdlettergevoelig is.

Bij registratie wordt gebruik gemaakt van de RegisterPropertyEditor procedure. Hierbij dient te worden opgegeven wat de type informatie is van de property waarvoor de editor geschreven is. In dit geval is het een property van het type TComponentName, dus daarvan wordt de type informatie doorgegeven. Tevens moet worden aangegeven voor welke componentclass de property editor bedoeld is. In ons geval is de editor voor elke TComponent descendant bedoeld. De volgende parameter is de naam van de property die het betreft: in ons geval dus 'Name'. Als laatste parameter wordt de property editor zelf doorgegeven.

De simpele versie

De simpele versie van de name property editor is nu klaar. De editor kan voor ieder component een prefix registreren en voor bekende componenten kan de prefix worden geselecteerd uit de lijst van prefixen. Het package waarin de broncode staat voor de simpele versie van de property editor kan worden geïnstalleerd net als ieder ander designtime package. Let op: de editor is niet perfect, want allerlei controles op geldigheid van de prefix en dergelijke worden nog niet gedaan.

Prefix formules

De volgende stap is het toevoegen van de waarde van een property in de naam van het component. De meest eenvoudige oplossing is het maken van een formule voor een prefix en een property waarde. De mogelijkheid wordt toegevoegd om een + symbool te gebruiken in de opgegeven prefix, waarna de naam van een string property volgt. De inhoud van het ini bestand zou er dan als volgt uit kunnen zien:

[Prefixes]
  TButton=btn+Caption
  TPanel=pnl+Caption
  TDataSource=dts
  …

De property editor kan worden uitgebreid met twee methods: EvaluatePrefix en StripInvalidChars. De eerste evalueert de prefix tot een prefix en eventuele waarde van een string property, de tweede zorgt dat de waarde van de string property alle geldige tekens bevat voor het benamen van een component.

Om de string property uit te lezen wordt gebruik gemaakt van de unit typinfo (terug te vinden in de Delphi map onder Source\Rtl\Common). De functies IsPublishedProp wordt gebruikt om te controleren of de property die is opgegeven ook een geldige published property van het component is. De functie GetStrProp wordt gebruikt om de waarde van de property op te halen.

De complete versie

De complete versie van de name property editor is nu klaar. De editor kan voor ieder component een prefix formule registreren en voor bekende componenten kan de prefix worden geselecteerd uit de lijst van prefixen. Het package waarin de broncode staat voor de complete versie van de property editor kan worden geïnstalleerd net als ieder ander designtime package. Let op: ook deze editor is niet perfect, want allerlei controles worden nog niet gedaan.

Conclusie

Het is duidelijk dat de Delphi open tools api op eenvoudige wijze toestaat handige IDE extensies te maken die het leven vereenvoudigen. En het mooiste is: alles kan worden geprogrammeerd in de bekende Delphi syntax. De name property editor is een voorbeeld hiervan dat menige ontwikkelaar zal aanspreken vanwege zijn eenvoud en toepasbaarheid.