Delphi tools: Hide non-visual components
Erik 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.
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. 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.
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.
|