Delphi tools - Hide non-visual componentsGeplaatst door Baldo op 13-04-03Erik Stok (aka Baldo)
In het vorige
artikel uit de serie Delphi tools heb ik een voorbeeld gegeven
van het gebruik van een expert om het menu in de Delphi IDE gemakkelijk uit
te kunnen breiden met eigen opties. In dit artikel ga ik gebruik maken van de
expert om een nieuwe optie in het menu te maken: Hide non-visual components.
Het idee
De applicaties die ik over het algemeen bouw zijn GUI applicaties. Hierbij
zijn forms gevuld met allerlei visual controls. Hoewel het uitlijnen heel gemakkelijk
gaat (zie het artikel over de align
component editor), wil ik af en toe toch nog wat aan de lay-out van het
form wijzigen. En dan liggen die non-visual components me toch steeds in de
weg.
Het moet toch mogelijk zijn om die even te verbergen zodat ik aan de slag kan
met de visual components. En als ik dan klaar ben dan moet ik ze toch ook weer
eenvoudig kunnen tonen.
Een component editor op het form maken is niet echt de oplossing, want zodra
het form compleet gevuld is, bijvoorbeeld met panels, dan kan ik niet bij de
component editor. Een property editor is ook geen oplossing want het betreft
hier geen property. Zo kwam ik terecht bij de in het vorige artikel beschreven
expert om een nieuw menu item toe te voegen.
Grenzen van de OTA
Als eerste ben ik eens op zoek gegaan naar wat de Delphi open tools api (OTA)
allemaal aanbiedt om bij de components op het form te komen. Ik wil eigenlijk
een referentie hebben naar het form dat designtime wordt weergegeven. Natuurlijk
kan ik in een designtime package gebruik maken van de application variabele
en van daaruit een zoektocht starten naar de form designer, maar de open tools
api biedt vast wat hulp.
Het blijkt dat via het IOTAModuleServices interface van de BorlandIDEServices
een referentie naar de huidige openstaande module kan worden verkregen. De eerste
winst is al geboekt: ik weet de openstaande module. Nu de form designer daarvan
te pakken zien te krijgen.
Na wat speurwerk kwam ik er achter dat een module meerdere files kan beslaan.
Als ik een nieuwe applicatie aanmaak in Delphi en daarin zit de module unit1,
dan bestaat deze module uit unit1.pas, unit1.dfm en unit1.ddp. Via het IOTAModule
interface is het mogelijk om een referentie naar de editor van een specifieke
file van een module te krijgen. Ik ben alleen geïnteresseerd in de form
editor, dus als ik zou controleren of een editor het IOTAFormEditor interface
implementeerd, dan weet ik dat ik de form editor van de module te pakken heb.
Dit alles resulteert dus in de volgende code:
function GetFormEditor: IOTAFormEditor; var Module : IOTAModule; Editor : IOTAEditor; FormEditor: IOTAFormEditor; i : Integer; begin // Standaard resultaat FormEditor := nil;
// Bepaal de huidige module Module := (BorlandIDEServices as IOTAModuleServices).CurrentModule;
// Als die gevonden is, zoek dan naar de editors ervan if Assigned(Module) then begin
// Loop door de module files for i := 0 to Module.GetModuleFileCount - 1 do begin // Probeer een referentie naar de module file editor te krijgen Editor := Module.GetModuleFileEditor(i);
// Als referentie niet gevonden kan worden, laat dan maar zitten if not Assigned(Editor) then Break;
// Anders, controleer of het ook daadwerkelijk een form editor is. if (Editor.QueryInterface(IOTAFormEditor, FormEditor) = S_OK) then Break; end;
end;
// Geef resultaat terug en laat alle interface references los Result := FormEditor; Module := nil; Editor := nil; FormEditor := nil; end;
Een referentie naar de form editor is een stap dichter bij het uiteindelijke
doel: de non-visual components van dit form benaderen. Mijn eerste gedacht was
om voor alle components van het form te controleren of het een non-visual component
betrof, en zo ja de visible property daarvan op false te zetten. Toen ik een
stuk geprogrammeerd had zag ik wat ik verkeerd aan het doen was. Een TDataSource,
een voorbeeld van een non-visual component, heeft helemaal geen visible property.
Ik moest niet het component hebben, maar het blokje dat de form designer gebruikt
om het component visueel te representeren! En daar biedt de open tools api geen
faciliteiten voor.
Good old windows
Door wat te zoeken op het web vond ik de oplossing (met dank aan google).
De visuele representatie van een non-visual component heeft een window handle.
Tevens is de classname van dit window TContainer. Met deze kennis is het mogelijk
om door alle window handles te lopen waar het form owner van is en te controleren
of het een non-visual component is. Ik gebruikte de functie GetWindow om door
de Windows heen te lopen, maar PsychoMark wees me in het forum erop dat de beste
functie die je daarvoor kunt gebruiken EnumChildWindows is.
Het enige wat dan nog moet gebeuren is het zetten van visible.
En daar heeft de windows api een functie voor genaamd ShowWindow. Door
aan ShowWindow een handle en een boolean (True is show, False is hide)
mee te geven kan de visibility van een window aan en uit gezet worden.
De routine
Al het bovenstaande resulteert dan in de volgende code:
function ToggleNVsCallback(hWnd: HWND; lParam: LPARAM): BOOL; stdcall; var cClassName: array[0..255] of Char; begin // Bepaal de class van het "window" FillChar(cClassName, SizeOf(cClassName), #0); GetClassName(hWnd, @cClassName, SizeOf(cClassName));
// Als het een TContainer is dan is het een non-visual component. // Toggle deze. if String(cClassName) = 'TContainer' then
begin
if not IsWindowVisible(hWnd) then ShowWindow(hWnd, SW_SHOW) else ShowWindow(hWnd, SW_HIDE);
end;
// Geef aan Windows door dat we door willen gaan met het volgende childwindow Result := True; end;
procedure ToggleNVs(Form: TCustomForm); begin // Vraag alle child window handles aan en voer voor elk van de een procedure uit EnumChildWindows(Form.Handle, @ToggleNVsCallback, 0); end;
Al met al een vrij eenvoudige routine die de hele open tools api links laat
liggen om zijn doel te bereiken. Gewoon windows api calls zijn voldoende.
Het menu item
De laatste stap is het toevoegen van een menu item waarmee deze nieuwe functionaliteit
kan worden gestart. In het vorige artikel kwam naar voren dat een TIdeMenuObject
afgeleide maken voldoende is. Zie de unit uIdeMenuNvComponents.pas
hoe dat er uiteindelijk in code er uit is komen te zien. Het registreren van
het nieuwe item kan worden gedaan in de constructor van de IdeMenuExpert. Natuurlijk
moet dan niet worden vergeten om uIdeMenuNvComponents in de uses clause van
de IdeMenuExpert op te nemen.
Mocht je zelf geen zin hebben om de IdeMenuExpert aan te passen dan kun je
ook het package downloaden waarin ik deze uitbreiding
al heb toegevoegd.
Conclusie
Doordat we in het vorige artikel een goede basis hadden gelegd voor het uitbreiden
van het Delphi menu, is het een fluitje van een cent gebleken om een eigen feature
aan Delphi toe te voegen. En hoewel de open tools api voor het verbergen van
non-visual components geen opties biedt, blijkt wederom dat de alledaagse kennis
van Delphi en Windows voldoende is om een mooi stuk software neer te zetten. |