Visual Objects beschikt over de databrowser voor het kolomsgewijs weergeven van veldinformatie. Gebruikt u die nog steeds? En heeft u Visual Objects 2.6? Dan is het de hoogste tijd de databrowser aan de kant te zetten. Bij uw 2.6 VO zit een rijk uitgerust gratis versie van bBrowser, een uit Duitsland afkomstige 'third party browser'. Zoals de meeste gratis meegeleverde third party tools is er tegen een geringe meerprijs (39,-) een versie met nog meer features te koop. Deze features zijn Drag & Drop, Info Values, Multiple Selection, Owner Draw Support en Variable Row Height. Bovendien is er technische support bij inbegrepen. Daarnaast kan voor 79 euro de Professional versie, met een SDK en dus source code, worden gekocht. Maar wie het bij de standaard versie wil laten heeft al enorm veel functionaliteit tot de beschikking. Je ziet, dat bBrowser een de facto standaard aan het worden is bij de ontwikkelaars, net zoals de meeste ontwikkelaars CA's standaard rapport generator al vervangen hebben door Report Pro of Crystal Reports.
Een andere tool is eveneens gratis en wordt ook intensief gebruikt. Het is de FabPaintControl van de Fransman Fabrice Foray. Deze programmeur heeft op zijn website http://www.fabtoys.net een indrukwekkend aantal voor Cavo geschikte utilities gezet. En het zijn nogal wat utilities die een programmeur met gemiddelde kennis niet zomaar maken kan, zoals Fab Twain (om scans via een Twain driver in te lezen) Fab Zip (om zipbestanden te maken en te lezen) of Fab Paint.
Sneller inzetten
Het nadeel van dit soort tools is dat je tijd moet besteden om ze te leren kennen en vooral: om ze in je bestaande applicatie te implementeren. In een nieuwe reeks webartikelen, waarvan ondergetekende een deel gaat schrijven, proberen wij u door de valkuilen van bepaalde Cavo classes te loodsen zodat u deze met minimale inspanning en eenvoudig te begrijpen voorbeeldcode kunt implementeren. Als eerste volgt een inleidend bBrowser artikel. Hiermee kunt u snel een browser opzetten en ook snel de standaard Cavo browser weergave vervangen door de bBrowser. Vooral dat laatste is een minder bekende mogelijkheid. Daarna volgt een artikel over het toepassen van de Fab Paint control, de FabTwain library en de FabZip library.
De Fab Paint control
Op genoemde website is dit allemaal niet zo snel terug te vinden. Gebruik hiervoor de Search keuze van de site en type in Fab. Alle downloads zijn dan wel zo'n beetje terug te vinden. De Paint control kan dynamisch worden toegewezen aan uw eigen scherm. Vervolgens kunt u er diverse grafische bestanden in weergeven met een aantal handige opties. Om de Fab Paint lib te gebruiken moet u eerst de library Fab PaintLib inlezen, gevolgd door Fab PaintLib Control. Laatst genoemde library gebruik Fab PaintLib en op uw beurt include u weer Fab PaintLib Control in uw applicatie. In dit specifieke geval beschikt u echter niet over de gehele source. U moet namelijk een DLL meeleveren FabPaint.DLL of FabPaintGif.DLL als u ook het GIF formaat wilt ondersteunen. Deze DLL's zijn niet bijgewerkt bij het uitkomen van versie 2.6 van Cavo. In dit geval is dat geen probleem. Een blik op de DLL's via Windows 2000 Support Tools' Dependency Walker leert, dat de ge-include libraries uitsluitend Microsoft Windows producten zijn. De DLL wordt geacht de CaPaint.dll te vervangen.
De implementatie is zeer eenvoudig. In uw applicatie schrijft u:
LOCAL oPLC AS FabPaintLibCtrl
gevolgd door
oPlc:=FabPaintLibCtrl{SELF,199,Point{},Dimension{},,TRUE)
en voor het weergeven van een bepaalde afbeelding cFile (pad+bestandsnaam):
oPlc:Image:=FabPaintLib{ cFile, SELF }
bBrowser
De bBrowser installatiebestanden moeten niet van de Cavo26 CD worden gehaald. Hierin zit namelijk een fout. Ga hiervoor naar de website
http://www.bBrowser.com/english/bBrowser/patches.htm.
Installatie
De patch voor 1.4 limited is in feite het volledige programma - en bevat ook de laatste bug fixes (in dit geval t/m februari 2003). Na installatie van deze bBrowser is er een subdirectory bBrowser gemaakt waar in de subdirectory source staat met de prototype libraries en nog enkele subdirectories (samples, help). Deze libraries moeten in uw project worden ingelezen, in de volgorde bDefines, bSystem, bServer, bGui, bBrowser. Die volgorde kunt u natuurlijk ook aan het VOPP overlaten. De bijbehorende DLL's (bSystem, bServer, bGui, bBrowser). staan ook al gereed, in de BIN subdirectory van uw Cavo 26 installatie. Deze DLL's moeten aan de klant worden meegeleverd. Het programma Wedsetup.exe (met daarbij een .sysbestand) staat ook in die BIN subdirectory en past het bestand Cavowed.inf aan met een sectie
[CONTROL:CUSTOMCONTROL:BBrowser].
Het gevolg van het draaien van Wedsetup ziet u direct bij het opstarten van Cavo. Het pallet met controls is achteraan met een uitgebreid, namelijk de bBrowser. De eerste toepassing bestaat dan ook uit het maken van een window, waarin u ergens dit bBrowser control zet, op dezelfde wijze waarop u een sle of een subdatawindow zou plaatsen. Uw applicatie moet wel de bBrowser libraries includen via Properties/Libraries. Daarna kijken we hoe we een bBrowser kunnen toepassen zonder eerst een control te hoeven plaatsen, dat ter vervanging van de BrowseView.
De bBrowser control toepassen
Nu doet de bBrowser control op zichzelf niets. Dus gaan we wat code schrijven die we gemakkelijk kunnen gebruiken voor alle genoemde toepassingen. Om te beginnen subclassen we de bBrowser en laten we het control erven van deze subclass:
CLASS IC2bDataBrowser INHERIT bBrowser
We kunnen dan een init method schrijven. Deze ziet er zo uit:
METHOD Init(oOwner, uID, oOrigin, oDimension, kStyle) CLASS IC2bDataBrowser
IF PCount()<>1
SUPER:Init(oOwner, uID, oOrigin, oDimension, kStyle)
ELSE
IF IsInstanceOf(oOwner, #DataWindow) .or. ;
IsInstanceOf(oOwner, #DataDialog)
SUPER:Init(oOwner:__FormWindow, 1000, Point{0, 0},; Dimension{0, 0}, WS_CHILD+WS_TABSTOP)
SELF:CommandOwner := oOwner
ELSE
SUPER:Init(oOwner, 1000, Point{0, 0}, Dimension{0, 0}, WS_CHILD+WS_TABSTOP)
ENDIF
SELF:SetDefaults()
ENDIF
In het geval van een control doet deze init method overigens effectief niets meer dan self:SetDefaults aanroepen, want in de 3e regel wordt namelijk slechts de super:init aangeroepen. Zelfs de SetDefaults zou kunnen worden overgeslagen. Deze ziet er als volgt uit:
METHOD SetDefaults CLASS IC2bDataBrowser
SELF:Editable := TRUE
SELF:HorizontalScrollbarMode:=#Auto
SELF:VerticalScrollbarMode :=#Auto
SELF:ViewLogicAs :=#Checkbox
SELF:CurrentMode :=#Line
SELF:GridInEmptyArea :=FALSE
SELF:EnableSelector(FALSE)
SELF:EnableColumnMove(TRUE)
SELF:EnableColumnResize(TRUE)
SELF:EnableCaptionClick(TRUE)
SELF:EnableRowResize(FALSE)
SELF:EnableRowHeightVariable(FALSE)
SELF:AutoClose:=FALSE
En daarmee zien we gelijk wat voor eigenschappen er zoal aan de bBrowser control kunnen worden toegekend. De meeste opdrachten spreken voor zich. Laat u deze intialisatie weg, dan worden de bBrowser defaults gebruikt; u kunt uiteraard een of meerdere eigenschappen na de initialisatie aanpassen, zowel direct in de eigenschappen van het control via de editor, of via code.
Velden weergeven
De weer te geven velden zijn eenvoudig door te geven. Zo maak ik gebruik van 3 arrays, die ik aan een zelfgeschreven method toevoeg. De array aSymFields bevat de symbolic names (dus met een # ervoor) van de direct weer te geven veldnamen. De array aSymLogicFields bevat eventueel op de gewenste plaatsen enkele extra velden, waarvan de inhoud niet rechtstreeks uit de database komt, bijvoorbeeld omdat er mee gerekend moet worden, of dat ze via een keyveld uit de weergegeven database uit een andere database moeten komen. Ook de kopjes staan in een array. Dus bijvoorbeeld:
aSymFields:={#Roepnaam,#Toestel}
aSymLogicFields:={#Roepnaam,#Naam,#Toestel}
Nu nemen we de database t.b.v. de bBrowser control in gebruik (zet eventueel eerst de database odbcontact in de juiste order):
SELF:oDCbBContact:Use(odbContact,aSymFields)
SELF:oDCbBContact is de naam van de bBrowser control in het scherm. Use is een standaard method van de class bBrowser. Hiermee zijn de database en 2 velden beschikbaar in de bBrowser control. In deze (contactpersonen) database is de naam in stukjes opgebouwd. We willen ook de volledige naam zien, en daarvoor is het logische veld #naam toegevoegd. Die naam willen we tussen de roepnaam en het toestel zien. We gebruiken hiervoor de opdracht:
SELF:oDCbBContact:InsertNewColumn(#Naam,2,{"o"o:ReturnName(o:recno)})
#Naam is de in aSymLogicFields opgegeven symbolische naam van het invoegveld en 2 de positie waar we het veld willen zien. Het codeblok waarin ReturnName geeft als returnvalue de volledige contactpersoonsnaam terug. ReturnName kan zowel een functie zijn, als een method van de class van de databaseserver die we bij Use hebben opgegeven. Er kunnen ook paramaters worden meegegeven, in dit voorbeeld is dat het actuele recordnummer, zodat direct in een locale instantiatie van de contactendatabase in ReturnName ook het afgebeelde record kan worden opgezocht.
De method InsertNewColumn ziet er als volgt uit:
METHOD InsertNewColumn(sLabel,nPos,oBlok) CLASS IC2bDataBrowser
LOCAL oColumn AS bDataColumn
oColumn := bDataColumn{SELF,SELF:Server,oBlok,#Expression)
oColumn:HyperLabel := HyperLabel{sLabel}
oColumn:CalculateWidth() // calculate width of column
SELF:AddColumn(oColumn) // add column in browser
SELF:OpenColumn(oColumn, nPos) // show column at first
De kopjes erboven geven we als volgt op.
SELF:oDCbBContact:SetCaptions,aSymLogicFields,{;
{"Voornaam",90},;
{"Naam contact",200},;
{"Tel.",120}, })
Met de paramaters aSymLogicFields en een array met kopjes en breedtes worden de kopjes geplaatst. De breedte moet in points worden opgegeven.
Deze method ziet er weer als volgt uit:
METHOD SetCaptions(aufield,auInfo) CLASS IC2bDataBrowser
LOCAL oColumn AS bDataColumn
LOCAL nField AS DWORD
LOCAL nWidth AS DWORD
LOCAL lWidth AS LOGIC
LOCAL cCaption AS STRING
// Loop alle velden af
FOR nField:=1 UPTO ALen(auField) // Set the vars
nWidth :=0
lWidth :=FALSE
cCaption:=auInfo[nField,1]
nWidth:=auInfo[nField,2]
oColumn :=SELF:GetColumn(auField[nField]) // Get the column
IF !Empty(oColumn)
IF Empty(cCaption) // Set caption
IF oServer:FieldPos(auField[nField])<>0
oColumn:Caption :=SELF:Server:Fieldspec(auField[nField]):Caption
END
ELSE
oColumn:Caption:=cCaption
END
IF !Empty(nWidth)
oColumn:Width:=nWidth // Set width
END
END
NEXT nField
IF lWidth // Did the width change?
SELF:Recalculate()
END
RETURN
Het feest kan beginnen! De daadwerkelijke weergave van de browser regelen we als volgt:
SELF:oDCbBContact:DisplayTheColumns(aSymLogicFields)
Waarbij die method er als volgt uitziet:
METHOD DisplayTheColumns(aFields) CLASS IC2bDataBrowser
SELF:Clear(FALSE)
SELF:OpenColumn(aFields)
Bovenstaande regels laten zich het handigst verzamelen in een method, die u bijvoorbeeld in de postinit aanroept van het scherm, waarin de bBrowser control staat:
METHOD DefineContactBrowser(oBrowser) CLASS RelatieVenster
LOCAL aSymFields AS ARRAY
LOCAL aSymLogicFields AS ARRAY
aSymFields:={#Roepnaam,#Toestel}
aSymLogicFields:={#Roepnaam,#Naam,#Toestel}
SELF:oDCbBContact:Use(oas:odbContact,aSymFields)
SELF:oDCbBContact:Server:OrderScope(TOPSCOPE,SELF:Server:groep+SELF:Server:nr)
SELF:oDCbBContact:Server:OrderScope(BOTTOMSCOPE,SELF:Server:groep+SELF:Server:nr)
SELF:oDCbBContact:SetCaptions,aSymLogicFields,{;
{"Voornaam",90},;
{"Naam contact",200},;
{"Tel.",120},;
})
SELF:oDCbBContact:DisplayTheColumns(aSymLogicFields)
In een vervolgartikel zien we hoe we kolommen of inhoud kunnen kleuren, icons aanbrengen en bijzondere inhoud tonen.
De databrowser vervangen
Maar wat te doen als we niet eerst een control willen neerzetten en gewoon een bBrowser willen zien als we de TableView kiezen? Ook dat kan! De stappen zijn als volgt:
- Voeg per datawindow een autocreatebrowser toe. Zodra alle databrowsers vervangen zijn kunt u de class vervangen door uw eigen subclass waarvan uw datawindows erven zodat er maar een method over hoeft te blijven. Zie de code hieronder. Deze code zorgt ervoor, dat de ViewTable wordt 'omgeleid' naar uw DefineBrowser method per venster.
- Plaats de opdracht
SELFBrowserClass = #IC2bDataBrowser
in de postinit van het tabwindow of datawindow (dus het window waarin de standaard browser gedefinieerd staat).
- Haal de oude browser en eventuele daarop betrekking hebbende opdrachten, zoals GBSREADONLY, weg.
- Schrijf een vergelijkbare initialisatie als bovengenoemde DefineBrowser. Gebruik bijvoorbeeld DefineBrowser voor de default tabelweergave en voeg DefineXXbrowser methods toe voor iedere control xx en roep dit aan in de postinit
- Voeg in de QueryClose toe:
IF IsInstanceOf(SELFbrowser,#ic2bdatabrowser)
SELFBrowserDestroy() // Nodig ivm bBrowser 7-3-2003
ENDIF
- Een eventuele SetOrderScope zet u in de DefineBrowser en ook in de Notify method.
METHOD __AutoCreateBrowser() CLASS RelatieTab
// DEZE WEGHALEN ALS ALLE BROWSERS VERVANGEN ZIJN EN 1 METHOD // VAN ICDW OVERHOUDEN
// create browser
IF IsNil(oGBrowse)
SELF:oGBrowse := CreateInstance(symBrowserClass, SELF)
ENDIF
IF SELF:sCurrentView=#ViewSwitch
Send(SELF:oGBrowse, #__NOTIFYChanges, GBNFY_VIEWSWITCH)
ELSEIF SELF:sCurrentView=#BrowseView
Send(SELF:oGBrowse, #__NOTIFYChanges, GBNFY_VIEWASBROWSER)
ELSEIF SELF:sCurrentView=#FormView
Send(SELF:oGBrowse ,#__NOTIFYChanges, GBNFY_VIEWASFORM)
ENDIF
IF SELF:lLinked .and. IsInstanceOfUsual(SELF:oAttachedServer, #DataServer)
IF IsAccess(SELF:oGBrowse, #Server) .and. IsMethod(SELF:oGBrowse, #Use)
IF IsMethod(SELF,#DefineBrowser)
Send(SELF,#DefineBrowser,SELF:oGBrowse)
ENDIF
ENDIF
ENDIF
RETURN SELF
Ik hoop dat bovenstaande voorbeelden voldoende houvast bieden snel aan de slag te gaan met bBrowser en/of Fab en ga m’n best doen dit artikel in de loop van het jaar op te volgen.