Inleiding
Ongetwijfeld zijn de meeste lezers van het SDGN Magazine bekend met Visual Studio.NET 2003 en zullen velen van u software ontwikkelen met C# of met Visual Basic.NET. Misschien minder bekend is de mogelijkheid om met dezelfde ontwikkelomgeving ‘managed’ applicaties te ontwikkelen voor zogenaamde smart devices. Dit zijn apparaten die werken met het Windows CE.NET besturingssysteem. In principe betreft het hier een veelheid aan apparaten, variërend van PocketPC’s tot industriële besturingen. Veel functionaliteit die voor .NET ontwikkelaars op PC’s en op servers beschikbaar is, is in een uitgeklede versie ook beschikbaar voor device ontwikkelaars. Specifiek voor Windows CE.NET apparaten is het .NET Compact Framework verschenen. Binnen de wereld van de smart devices is platform onafhankelijkheid met de komst van het .NET Compact Framework een feit. Windows CE.NET kan namelijk werken op verschillende hardware omgevingen. Toch hoeft een .NET Compact Framework applicatie slechts één keer te worden ontwikkeld. Deze zal zonder verdere aanpassingen werken op Pentium, ARM, XScale, MIPS en SHx processors, dit alles dankzij JIT-compilatie. Vanuit hardware oogpunt bezien is Windows CE.NET dan ook een uitstekend besturingssysteem om van omgeving te kunnen migreren. Dat kost nog steeds wel enige inspanning, maar vanwege het feit dat Windows CE.NET slechts een klein deel aan hardware specifieke functionaliteit bevat, is dit zeker niet onmogelijk. Omdat het .NET Compact Framework echter slechts een deel van de functionaliteit van het .NET Framework bevat, zal het ongetwijfeld regelmatig voorkomen dat ontwikkelaars eigen/custom controls moeten ontwikkelen. Omdat hier iets meer werk mee gemoeid is dan bij het ontwikkelen van custom controls voor het .NET Framework, staan we in dit artikel stil bij het ontwikkelen van een custom control voor het .NET Compact Framework.
Veel functionaliteit die voor .NET ontwikkelaars op PC’s en op servers beschikbaar is, is in een uitgeklede versie ook beschikbaar voor device ontwikkelaars.
Migratie van eVB
Embedded Visual Basic was tot de komst van .NET de manier om applicaties te ontwikkelen voor handheld devices. eVB kenmerkte zich door een heel beperkte set van componenten waarmee eenvoudige applicaties ontwikkeld konden worden. Features als SOAP en XML waren standaard niet aanwezig, maar via 3rd party tools was dit platform nog wel hier en daar uit te breiden. Met de komst van het Compact Framework is het aantal componenten flink toegenomen en is de functionaliteit dusdanig uitgebreid dat het mogelijk is om serieuze toepassingen te ontwikkelen voor deze handheld devices.
Een .NET CF Custom control
Een custom control is een user interface element (component) dat, indien opgenomen in de toolbox van Visual Studio.NET, met behulp van drag-and-drop op een Windows Form kan worden geplaatst. Een custom control zal normaal gesproken worden afgeleid van de System.Windows.Forms.Control class. Deze class bevat gemeenschappelijke basisfunctionaliteit voor custom controls. In een aantal stappen zullen we een simpele component voor het .NET Compact Framework gaan ontwikkelen, waarbij we beginnen met een runtime versie van de component die we later ook voor eenvoudig gebruik zullen integreren in Visual Studio.NET 2003. Als voorbeeld kiezen we een ColorPicker, een gekleurd vierkantje dat deel uit kan gaan maken van een kleurselectie dialoog. Met verschillende instanties van deze component is uiteindelijk een dergelijke dialoog te maken.

Fig. 1: Een kleurselectie dialoog op basis van de ColorPicker custom control
De custom ColorPicker control bestaat dus uit een klein gekleurd vierkantje. De grootte en de kleur van de component zijn met behulp van properties instelbaar. De ColorPicker tekent zichzelf op de juiste locatie in de juiste kleur en houdt ook bij of hij op dit moment geselecteerd is. De “eigenaar” van de component kan events ontvangen zoals muisklikken. De source code van de ColorPicker is relatief eenvoudig (zie listing 1). Het meeste werk wordt uitgevoerd tijdens het tekenen van deze component. In de OnPaint method van de ColorPicker wordt een gekleurd vierkantje getekend en wordt er een extra kader omheen geplaatst voor het geval het betreffende vierkantje is geselecteerd. Daarnaast bevat de ColorPicker extra functionaliteit om te detecteren dat deze component is geselecteerd. Met behulp van de OnGotFocus en OnLostFocus methods wordt de component geforceerd opnieuw getekend, al dan niet voorzien van het extra kader. Dat is in principe de complete functionaliteit van de ColorPicker. Als we de ColorPicker van listing 1 met behulp van Visual Studio.NET 2003 compileren, resulteert dit in een herbruikbare component die binnen grafische gebruikersinterfaces kan worden toegepast. Om de ColorPicker op deze manier te kunnen gebruiken moet in een Windows Forms applicatie een referentie naar de ColorPicker worden opgenomen. Dit gebeurt door in de solution explorer met de rechter muisknop te klikken op “references”, waarna de ColorPicker via een dialoog kan worden toegevoegd aan de solution (zie figuur 2). Het is hiermee echter nog niet mogelijk tijdens ontwerp van een Windows Form via drag-and-drop deze component vanuit de toolbox naar de gewenste locatie te slepen. De component is al wel te gebruiken, maar kan alleen maar in code binnen de applicatie worden aangemaakt.

Fig. 2: Toevoegen van referenties aan de solution
Verschillen tussen ontwikkelomgeving en target
Om de component te kunnen opnemen in de toobox van Visual Studio.NET moet nog wat extra code worden toegevoegd. Hier ligt tevens de uitdaging. De uiteindelijke ColorPicker werkt op een Windows CE.NET apparaat dat geen weet heeft van Visual Studio.NET. Sterker nog, het apparaat heeft ook geen weet van het .NET Framework, omdat binnen Windows CE alleen het .NET Compact Framework beschikbaar is. Om de ColorPicker toch te kunnen opnemen in de Visual Studio.NET toolbox moet de component voor het .NET Framework geschikt worden gemaakt, omdat Visual Studio.NET zelf gebruik maakt van het .NET Framework. Daarom maken we twee verschillende versies van de component, één die geschikt is voor gebruik binnen Visual Studio.NET en één die geschikt is voor gebruik binnen een Windows CE.NET applicatie. Uiteraard zorgen we wel voor hergebruik van code. Dit is een groot verschil met het maken van custom controls voor het .NET Framework, omdat hier de uiteindelijke runtime omgeving gelijk blijft voor zowel Visual Studio als voor de applicatie. Een extra moeilijkheid is het feit dat een component binnen Visual Studio.NET niet tegelijkertijd voor zowel het .NET Compact Framework als voor het .NET Framework kan worden gecompileerd. Vandaar dat de versie van de component die wordt gebruikt voor integratie met Visual Studio.NET apart, via een batch bestand, wordt gecompileerd. Er is een groot voordeel aan het feit dat er twee verschillende versies van een component moeten worden gemaakt. Enerzijds kunnen we een component zo klein en efficient mogelijk maken voor gebruik binnen een Windows CE.NET omgeving, anderzijds kan dezelfde component wel worden voorzien van allerlei functionaliteit om het leven van de ontwerper eenvoudiger te maken. Immers, de designversie van een component bestaat alleen maar op de ontwikkelmachine.
De uiteindelijke control moet niet binnen Visual Studio.NET op de ontwikkelmachine werken, maar op een andere omgeving met het Windows CE.NET besturingssysteem
Designer support
In onze eigen source code moeten wat aanpassingen gemaakt worden om de designer ondersteuning binnen Visual Studio.NET 2003 te activeren. Allereerst moeten we opgeven welke design versie van de ColorPicker correspondeert met een runtime versie. Hiervoor gebruiken we via conditionele compilatie een compiler attribute, maar alleen als we de designversie van de ColorPicker maken. Ook maken we gebruik van extra functionaliteit voor componentondersteuning. Deze functionaliteit is ondergebracht in de System.ComponentModel namespace.
De volledige code van de ColorPicker, maar nu met design support is terug te vinden in listing 2. In het onderstaande code fragment is alvast te zien hoe we met behulp van conditionele compilatie een extra namespace kunnen toevoegen, en hoe we met behulp van een attribute aangeven welke versie van onze eigen component moet worden gemaakt voor latere opname in Visual Studio.NET.
#if FULLFRAMEWORK_DESIGNER
using System.ComponentModel;
#endif
#if FULLFRAMEWORK_DESIGNER
[assembly: System.CF.Design.RuntimeAssembly
("ColorPicker, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null")
]
#endif
Het gedrag van de ColorPicker tijdens design time is helemaal aan te passen door het toevoegen van extra attributes. Zo kunnen properties tijdens design time al dan niet zichtbaar en wijzigbaar gemaakt worden voor de ontwerper en kunnen bepaalde properties default waarden krijgen:
#if FULLFRAMEWORK_DESIGNER
[Browsable(false)]
[DevaultValue(Color.Red)
#endif
Ook kan Visual Studio.NET 2003 tijdens design time voor de betreffende component helemaal naar onze hand worden gezet door toevoeging van weer andere attributes. Zo kunnen we onze eigen IntelliSense toevoegen. Veelvuldig gebruikte attributes zijn:
#if FULLFRAMEWORK_DESIGNER
[
System.ComponentModel.Category("Layout"),
System.ComponentModel.Description
("Specifies the background color.")
]
#endif
Door deze attributen onmiddellijk voor een property van een eigen component op te nemen, zal de betreffende property tijdens design in het properties window van Visual Studio onder de gespecificeerde catagorie vallen en zal er extra informatie worden getoond over de property.
In het voorbeeld hebben we gekozen voor de meest eenvoudige situatie. Onze ColorPicker heeft design time support en alle properties worden beschikbaar gesteld aan de ontwerper. Het is wel belangrijk er rekening mee te houden dat geen Windows CE.NET specifieke code wordt opgenomen in de designer versie van een component. Indien gebruik gemaakt wordt van P/Invoke om native code binnen een component te gebruiken is dit extra oppassen, omdat de Win32 API’s in een Windows CE.NET omgeving zich in andere DLL’s bevinden dan in een Windows XP of Windows 2000 omgeving. Ook hiervoor is het dus noodzakelijk gebruik te maken van conditionele compilatie. Dit valt echter buiten de scope van dit artikel, omdat we hier slechts een eenvoudige component maken.
Om de zojuist gemaakte component te kunnen opnemen in Visual Studio.NET moet niet alleen extra code worden toegevoegd, maar moet de component ook op een bijzondere manier worden gecompileerd. Tijdens compilatie moet gebruik gemaakt worden van speciale versies van .NET Compact Framework assemblies, uitsluitend bedoeld om integratie binnen Visual Studio.NET mogelijk te maken. Deze assemblies zijn:
Deze zijn allemaal te vinden in de CompactFrameworkSDK\v1.0.5000\Windows CE\Designer subfolder onder de Visual Studio.NET 2003 installatiefolder. Tijdens compilatie van de designer variant van onze component nemen we dan ook referenties op naar deze specifieke assemblies (zie listing 3). Na compilatie moeten de resulterende assemblies, dus onze eigen component en de designer variant ervan, gekopieerd worden naar de juiste folders onder de Visual Studio.NET 2003 installatie folder. Om hier geen fouten in te maken is het verstandig dat in hetzelfde batch bestand te doen (zie listing 3).
Toevoegen van de colorpicker in VS 2003
Na het maken van de designer versie van onze component blijkt nog steeds dat de component niet zichtbaar is in de ontwikkelomgeving. Hiervoor moet nog één laatste handeling worden uitgevoerd. De ontwikkelaar moet namelijk zelf binnen Visual Studio.NET opgeven een bepaalde component te willen opnemen in de workspace. Door met de rechter muisknop te klikken op Device Controls in de toolbox wordt het menu van figuur 3 zichtbaar. In de daaropvolgende dialoog kunnen we de design versie van onze component specificeren.

Fig. 3: Toevoegen van de design-time control aan Visual Studio.NET 2003
Deze is na het bouwen van de component met behulp van het eerder beschreven batch bestand terug te vinden in de CompactFrameworkSDK\v1.0.5000\Windows CE\Designer subfolder onder de Visual Studio.NET 2003 installatiefolder. Na het uitvoeren van al deze handelingen zijn we uiteindelijk in staat om eigen gemaakte componenten tijdens het ontwerp van de grafische gebruikersinterface met behulp van drag en drop toe te voegen aan een Windows Form, waarbij we tijdens design time ook de properties van de betreffende componenten kunnen wijzigen. Het uiteindelijke resultsaat binnen Visual Studio.NET is te zien in figuur 4.

Fig. 4: Design time experience met een zelfgemaakte component
Conclusie
Ontwikkelen van custom controls voor het .NET Compact Framework is niet veel moeilijker dan ontwikkelen van custom controls voor het .NET Framework. De grootste verschillen zitten in integratie met Visual Studio.NET, omdat de uiteindelijke control niet binnen Visual Studio.NET op de ontwikkelmachine, maar op een andere omgeving met het Windows CE.NET besturingssysteem zal moeten werken. Vandaar dat voor designer support met behulp van conditionele compilatie extra code moet worden toegevoegd die niet in de uiteindelijke component mag voorkomen. De mogelijkheden tot het maken van eigen componenten zijn echter identiek voor .NET Framework en .NET Compact Framework toepassingen. Gebruikers van de uiteindelijke component zullen helemaal geen verschil ervaren.
De complete listings waar in dit artikel naar verwezen wordt zijn vanwege ruimtegebrek niet in dit artikel opgenomen maar zijn apart te downloaden: