Powered by TCPDF (www.tcpdf.org)
Academiejaar 2013–2014
Faculteit Ingenieurswetenschappen en Architectuur Valentin Vaerwyckweg 1 – 9000 Gent
Uitbreiden van de huidige User Interface Designer in DiAS DMS/CRM.
Masterproef voorgedragen tot het behalen van het diploma van Master in de industriële wetenschappen: informatica
Nick BRONSELAER Promotoren: dr. Leen BROUNS Wim BAELEN (C&C) Begeleiders: Ilse HEIRWEGH (C&C) Miguel DERESE (C&C) Frederick LOYSON (C&C) Eline VAN DEN BERGHE (C&C)
Powered by TCPDF (www.tcpdf.org)
Academiejaar 2013–2014
Faculteit Ingenieurswetenschappen en Architectuur Valentin Vaerwyckweg 1 – 9000 Gent
Uitbreiden van de huidige User Interface Designer in DiAS DMS/CRM.
Masterproef voorgedragen tot het behalen van het diploma van Master in de industriële wetenschappen: informatica
Nick BRONSELAER Promotoren: dr. Leen BROUNS Wim BAELEN (C&C) Begeleiders: Ilse HEIRWEGH (C&C) Miguel DERESE (C&C) Frederick LOYSON (C&C) Eline VAN DEN BERGHE (C&C)
Woord Vooraf Voor we beginnen zou ik graag een paar mensen danken. Vooreerst mijn oprechte dank aan mijn ouders voor het geloof en financi¨ele steun die ze mij hebben gegeven. Vervolgens wil ik ook mijn promotoren en begeleiders bedanken. Leen Brouns wil ik bedanken voor haar steun en advies. Wim Baelen wil ik bedanken voor zijn advies en de mogelijkheid die hij mij gaf om deze masterproef te voltooien. Helga Naessens wil ik bedanken voor het nalezen van deze scriptie en het advies dat ze gaf. Natuurlijk wil ik ook Liesbeth Mabilde, Ilse Heirwegh, Frederick Loyson en de rest van de werknemers bij C&C bedanken voor de hulp die ze mij gaven als er problemen waren. Ten slotte wil ik ook nog mijn vrienden Elias Van den Broeck, C´eline Erauw, Pieter De Bisschop en Jan de Bisschop bedanken voor de ontspannende en luchtige momenten.
ii
Inleiding Als student Master of Science: industri¨ele wetenschappen - informatica moet er om af te studeren een thesis geschreven worden. Wat u hier zal lezen is het resultaat van de thesis: Uitbreiden van de huidige User Interface Designer in DiAS DMS/CRM. DIAS is een archiefprogramma voor Computers & Comunnications. De User Interface Designer is hier een onderdeel van. Deze User Interface Designer is heel primitief en gebruiksonvriendelijk. Er ontbreken meerdere functies die tegenwoordig vanzelfsprekend worden gevonden zoals multiselection. Het doel van de masterproef is deze designer uit te breiden en gebruiksvriendelijk te maken. De designer is geschreven in C# en maakt gebruik van de .NET controls van Windows. De databank maakt gebruik van OpenEdge Progress 4GL. In deze thesis wordt er eerst uitleg gegeven over de User Interface Designer en zijn belangrijkste klassen. Vervolgens bekijken we de databank en ten slotte worden de uitbreidingen behandeld.
iv
Inhoudsopgave Woord Vooraf
ii
Inleiding
iv
I
1
De User Interface Designer
1 Doelstelling 1.1 Wat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Doel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 2 2
2 Belangrijke klassen 2.1 Solution . . . . . 2.2 DIASBase . . . . 2.3 DIASUI . . . . . 2.4 DIASUIControls
3 3 3 4 4
II
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
Databank
6
3 Progress 4GL 3.1 Wat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Hoe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3 Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7 7 7 8
III
9
Uitbreidingen
4 Undo/Redo 10 4.1 Hoe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 4.2 Memento pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 5 Tabbladen 15 5.1 Ori¨entatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 5.2 Font & Kleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 5.3 Kindtab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
vi
6 Vormen 18 6.1 Eerste poging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 6.2 Tweede poging . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 7 Subtypes 22 7.1 Nieuwe Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 7.2 Oude Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 7.3 Databank . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 8 PropertyGrid 26 8.1 Wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 8.2 FilteredPropertyGrid . . . . . . . . . . . . . . . . . . . . . . . . . 26 9 Versiebeheer 9.1 Analyse . . . . . . 9.2 Databank . . . . . 9.3 Aanpassingen UID 9.4 Openen . . . . . . 9.5 Problemen . . . . . 9.6 Testgebruikers . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
28 28 28 32 32 34 34
10 Scrolling 36 10.1 Probleem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 10.2 Oplossing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 11 Conditioner 38 11.1 Voorbereiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 11.2 Conditioner & Conditie . . . . . . . . . . . . . . . . . . . . . . . 39 12 Kleine Uitbreidingen 40 12.1 Zoekbalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 12.2 Titel en Tooltip . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 12.3 Zoom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
IV
Andere
43
13 Na uitbreidingen 44 13.1 Handleiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 13.2 Testen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Besluit
45
V
48
Bijlagen
vii
Deel I
De User Interface Designer
1
Hoofdstuk 1
Doelstelling 1.1
Wat
De User Interface Designer (UID) is een onderdeel van de Digital Information and Archiving applicatie (DIAS DMS/CRM) van het softwarebedrijf Computers & Communications (C&C). Dias DMS/CRM stelt de gebruikers in staat om data te verzamelen in ´e´en groot archief. Deze data gaan van info over klanten tot rekeningen. De UID wordt ingezet om iedereen zijn eigen gepersonaliseerde bladspiegel te laten maken om deze data te lezen en in te voeren. De UID stelt de gebruiker in staat om eigen layouts te defini¨eren voor de weergave van: • documenten • projecten • zoekvensters • detailfiches • ...
1.2
Doel
De UID is nog vrij primitief en er missen een paar cruciale mogelijkheden om hem gebruiksvriendelijk te maken: • undo / redo • resize van componenten via drag en drop • sorteren van de controls in het paneel • multiselecteren van elementen in de UID • verticale tabbladen • tabbladen voor kleur en font • ... Het de bedoeling om een gebruiksvriendelijke UID te maken die voor een beginner intu¨ıtief te gebruiken is. 2
Hoofdstuk 2
Belangrijke klassen 2.1
Solution
Om alles duidelijker te maken, wordt er eerst een beeld geschetst van hoe de code eruit ziet. Zo kan er later naar verwezen worden zonder verwarring. De solution van de DIAS bestaat uit verschillende projecten. De UID gebruikt er drie: • DIASBase • DIASUI • DIASUIControls Nu wordt er gekeken naar de belangrijkste klassen uit deze drie projecten
2.2
DIASBase
In DIASBase bevinden zich de algemene klassen. UIComponent beschrijft de componenten die worden gebruikt in de UID. Hier worden de properties, events en values meegegeven aan een component. DiasProperties is een geserialiseerde klasse die wordt gebruikt indien een component extra properties nodig heeft. UITabFolder bevat alle gegevens van een tabfolder zoals zijn hoogte, titel,... UILayout beschrijft hoe een formulier eruit ziet. De componenten, tabfolders, settings,... worden allemaal hierin opgeslagen. UIObject is het object dat je aanmaakt met de UID. Dit bevat een layout, een (sub)type en de securitysettings UIRenderer zorgt ervoor dat de aangemaakte objecten worden gebruikt in DIAS. 3
DIASManManager voor het opvragen van proxy-, cache- en andere objecten, evenals vertalingen. Deze klasse voldoet aan het singleton-pattern en kan enkel opgeroepen worden via de public static Instance.
2.3
DIASUI
Hier bevinden zich alle panelen en vensters die kunnen aangepast worden via de UID. Ook de UID zelf bevindt zich hier. vwUIDesigner beschrijft de UID. Het bevat een UIObject en de bruikbare componenten voor dit object. Het heeft ook een paneel waarop je de componenten kan plaatsen. frmUIObjects bevat het formulier dat gebruikt wordt indien je een gemaakt UIObject wilt openen. frmNewLayout bevat het formulier waarmee je een nieuw UIObject aanmaakt. Je kan een type en subtype kiezen en vervolgens bepalen of het object tabbladen heeft of niet.
2.4
DIASUIControls
Hier bevinden zich alle custom controls en panelen die gebruikt worden over de hele solution. BasePanel is het paneel zonder tabbladen. Dit paneel wordt gebruikt bij de vwUIDesigner indien er gekozen is om geen tabbladen te gebruiken. Dit bevat de events, controls en settings van alles wat geplaatst is. TabbedPanel is een afgeleide klasse van BasePanel en wordt gebruikt indien er wel tabbladen nodig zijn. Dit bevat dus alles wat een BasePanel bevat + de events en data nodig om de tabbladen te onderhouden.
4
Figuur 2.1: De UID
5
Deel II
Databank
6
Hoofdstuk 3
Progress 4GL 3.1
Wat
Progress 4GL is een ontwikkelingstaal en -omgeving gericht op het maken van zakelijke applicaties. De taal is bedacht en wordt onderhouden door Progress Software Corporation (PSC). De taal, doorgaans geclassificeerd als een vierde generatie programmeertaal, gebruikt een engelsachtige syntaxis om het ontwikkelen van applicaties te vergemakkelijken.[1] De naam is gewijzigd in 2006 van PROGRESS (of Progress 4GL) naar OpenEdge Advanced Business Language (OpenEdgeABL).[2] OpenEdge ABL helpt ontwikkelaars om snel een applicatie te ontwikkelen met een ge¨ıntegreerde relationele database en ontwikkeltool. De gemaakte applicaties draaien op meerdere computersystemen (Unix, Windows) en bieden toegang tot een scala aan populaire gegevensbronnen (zoals XML, ODBC, etc.), zonder dat ontwikkelaars de onderliggende lagen hoeven te begrijpen. OpenEdge ABL en de OpenEdge database worden door een value-added reseller gebruikt om een uniek product te genereren. Door de eenvoudige gebruikersinterface, zal de eindgebruiker de onderliggende technische lagen vaak niet eens zien.
3.2
Hoe
Progress 4GL gebruikt een imperatieve programmeerstijl om toegang te krijgen tot een database, in plaats van embedded (SQL) expressies. De imperatieve stijl kenmerkt zich door het gebruik van FOR EACH expressies en FIND expressies. Daarnaast is er een variatie van expressies beschikbaar om formulieren te definiren en te gebruiken. Om binnen de expressies gebruik te kunnen maken van flow control bestaan er IF..THEN..ELSE statements en DO...WHILE statements. Hier is een voorbeeld van een query. ”FOR EACH u i o b j e c t WHERE u i o b j e c t . objectName EQ \”{0}\” AND (STRING( SubTypes ) MATCHES ’ ∗ { 1 } ∗ ’ OR STRING( SubTypes ) MATCHES ’ ∗ 0 ∗ ’ ) BY SubTypes DESCENDING”
{0} en {1} zijn variabelen die worden meegegeven.
7
3.3
Tabellen
In bijlage op pagina 49 bevindt zich het schema voor de databank. Dit is hoe de databank eruit zag voor de veranderingen. Er zijn een aantal velden toegevoegd, voornamelijk in UITabfolder en UIObject. In UITabFolder zijn de velden toegevoegd die nodig waren om de kleurveranderingen, ori¨entatie en kindtab te veranderen (zie pagina 15). In UIObject is het veld Subtype dan weer veranderd in Subtypes(zie pagina 22). Er is ook een tabel aan toegevoegd door de implementatie van versiebeheer (zie pagina 28)
8
Deel III
Uitbreidingen
9
Hoofdstuk 4
Undo/Redo 4.1
Hoe
Om een Undo/Redo functionaliteit te bekomen was het snel duidelijk wat er nodig zou zijn. Er moet een object aanwezig zijn dat de veranderde data verzamelt en een container om al deze objecten op te slaan. Een stack is de perfecte container om deze data op te slaan door zijn ”last in, first out”manier van ophalen. Om al deze objecten en containers op een ordelijke manier te gebruiken werd er gekozen om het memento pattern te gebruiken.
4.2
Memento pattern
Figuur 4.1: UML van het memento pattern Het memento pattern is een design pattern specifiek gemaakt om een object in een vorige staat te kunnen herstellen. Het patroon bevat 3 grote objecten. • De schepper (Originator) • Het moment (Memento) • De bewaarder (CareTaker) Figuur 4.1 toont de UML van dit pattern. De schepper is het object dat de momenten aanmaakt en een bepaalde staat kan teruggeven uit deze momenten. Het moment is een object dat een staat bijhoudt. Door dit moment later op te halen kan deze staat hersteld worden. De bewaarder houdt ´e´en of meerdere momenten bij en heeft ook een schepper. De bewaarder kan aan de schepper vragen om een moment van zijn staat te maken. Vervolgens zal de bewaarder verder werken en zich naar een andere 10
staat begeven. Indien de bewaarder zijn vorige staat terug wilt, kan hij aan de schepper vragen zijn staat terug te zetten. [3] Dit is een goed patroon, maar om het compatibel en overzichtelijker te maken is er besloten om een paar aanpassingen te doen.[4]
Figuur 4.2: UML van het gewijzigde memento pattern Op figuur 4.2 is te zien dat er een aantal uitbreidingen en veranderingen hebben plaatsgevonden. Om te beginnen is er de Memento-interface. In de originele interface bevat deze enkel een getter en setter om een staat terug te geven. Deze interface is te generisch. Een interface die een gedrag beschrijft is handiger dan een die enkel eigenschappen beschrijft. Daarom hebben we de Restore-methode toegevoegd. De Restore-methode wordt gebruikt om de staat die meegegeven wordt te herstellen naar de opgeslagen staat. De methode geeft ook een Memento terug. Dit is nodig om de Redo-functionaliteit te implementeren. Er zijn 2 manieren waarop we deze momenten zouden kunnen implementeren. • State Based • Action Based State Based is meer gebaseerd op het originele patroon. Deze zal de gehele staat opslaan in het moment, inclusief de variabelen die niet veranderd zijn sinds de laatste staat. Deze manier is voor de programmeur gemakkelijk aangezien er maar ´e´en klasse moet gemaakt worden. Er is echter een groot nadeel aan verbonden. Door het feit dat je alle variabelen moet opslaan, neemt deze methode veel geheugen in. Dit is acceptabel indien er weinig variabelen zijn of als er maar weinig momenten moeten aangemaakt worden. De vwUIDesigner is relatief groot en aangezien er een Undo/Redo functionaliteit ge¨ımplementeerd wordt, zal er voor elke actie die gebeurt een moment worden opgeslaan. Deze methode is dus niet bruikbaar. Action Based maakt gebruik van meerdere implementaties van de Mementointerface. Voor elke actie die er kan gedaan worden, moet er een andere interface 11
zijn. Hier is een voorbeeld van het moment dat moet worden opgeslaan als je een component verwijdert. c l a s s RemoveComponentMemento : ComponentMemento { private S t r i n g name ; private P o i n t l o c a t i o n ; private UIComponent comp ; private S t r i n g p a r e n t ; public RemoveComponentMemento ( S t r i n g name , UIComponent comp ) { t h i s . name = name ; l o c a t i o n = new P o i n t ( comp . L e f t , comp . Top ) ; t h i s . comp = comp ; t h i s . p a r e n t = comp . ParentName ; } public override IMemento
R e s t o r e ( I U I P a n e l t a r g e t ) { IMemento i n v e r s e = new AddComponentMemento ( name ) ; Control p = t a r g e t . getParent ( parent ) ; i f ( p == n u l l ) p = target ; t a r g e t . AddComponent ( p , comp , l o c a t i o n ) ; target . Refresh () ; return i n v e r s e ; } }
Hier worden enkel de nodige variabelen opgeslaan die nodig zijn om de actie ongedaan te maken. In geval van verwijderen wordt de naam, de component, de locatie van de component en zijn vader opgeslaan. Dit heeft natuurlijk veel minder geheugen nodig en is dus performanter. In de Restore-methode wordt er een nieuw moment aangemaakt. Dit is het tegenovergestelde moment dan hetgene dat hersteld wordt. Dit moment wordt teruggegeven om de Redo-functionaliteit te implementeren. Een ander moment dat wordt ge¨ımplementeerd is het CompoundMemento. Dit is een verzamelklasse voor momenten. Stel dat je meerdere componenten in ´e´en keer verandert. Dit moet gezien worden als ´e´en moment. Voor elke component zal zijn moment moeten worden toegevoegd aan de CompoundMemento. public void BeginCompoundDo ( ) { i f ( tempMemento != n u l l ) throw new I n v a l i d O p e r a t i o n E x c e p t i o n ( ” P r e v i o u s complex memento wasn ’ t commited . ” ) ; tempMemento = new CompoundMemento() ; } public void EndCompoundDo ( ) { i f ( tempMemento == n u l l ) throw new I n v a l i d O p e r a t i o n E x c e p t i o n ( ” Ending a non−e x i s t i n g complex memento” ) ; Do ( tempMemento ) ; tempMemento = n u l l ; }
12
Om deze momenten toe te voegen wordt eerst de BeginCompoundDo-Methode gebruikt. Deze zal een nieuw CompoundMemento-object aanmaken. Vervolgens worden alle aparte momenten in dit CompoundMemento gestoken. Als alle momenten verwerkt zijn, wordt de EndCompoundDo-methode opgeroepen. Deze zal het CompoundMemento-object opslaan en vervolgens terug op null zetten. Om al deze momenten op te slaan, wordt er gebruik gemaakt van een speciale stack, een RoundStack. De stack kan gezien worden als een cirkelvormige container die op het ”last in, first out”(LIFO) principe werkt. Op het moment dat de container vol is wordt het oudste object verwijderd en maakt het plaats voor het nieuwe. Dit is noodzakelijk bij programma’s waar lang in kan worden gewerkt zonder af te sluiten. Aangezien er bij elke actie momenten worden gecre¨eerd, gaan deze na verloop van tijd enorm veel geheugen gaan gebruiken als het aantal niet wordt beperkt. In de schepper zijn er ook aanpassingen gedaan. Om te beginnen worden de momenten nu hier opgeslaan in plaats van in de bewaarder. Er kan dus gezegd worden dat de schepper nu zowel bewaarder als schepper is. Dit komt door het feit dat de bewaarder in de UID de vwUIDesigner is. Deze klasse is al groot genoeg. In plaats van dat er in de caretaker een container van momenten en een schepper moeten beheerd worden, is dit nu nog enkel in de schepper. De schepper maakt en bewaart de momenten. De caretaker geeft de opdracht aan de schepper om ze te maken, verwijderen en uit te voeren. Deze momenten worden opgeslaan in RoundStacks. Om de momenten in RoundStacks te steken wordt de Do-methode gebruikt. public void Do( IMemento m) { i f ( inUndoRedo ) throw new I n v a l i d O p e r a t i o n E x c e p t i o n ( ” I n v o l k i n g do w i t h i n an undo / r e d o a c t i o n . ” ) ; i f ( tempMemento == n u l l ) { Do (m) ; } else { tempMemento . Add(m) ; } } private void Do ( IMemento m) { redoStack . Clear () ; undoStack . Push (m) ; }
De Do-methode wordt opgeroepen vanuit de vwUIDesigner. Deze zal eerst checken of de methode niet werd opgeroepen in een Undo/Redo actie. Vervolgens zal deze checken of er een CompoundMemento aangemaakt is, m.a.w. de BeginCompoundDo-Methode is aangeroepen geweest. Indien er een CompoundMemento is zal het moment worden toegevoegd aan het CompoundMemento,
13
anders wordt het moment opgeslagen in de undostack. public void Undo ( ) { i f ( tempMemento != n u l l ) throw new I n v a l i d O p e r a t i o n E x c e p t i o n ( ”The complex memento wasn ’ t commited . ” ) ; inUndoRedo = true ; IMemento top = undoStack . Pop ( ) ; r e d o S t a c k . Push ( top . R e s t o r e ( s u b j e c t ) ) ; inUndoRedo = f a l s e ; }
Als de Undo-Methode wordt gebruikt, wordt er eerst gekeken of er nog een Compoundmemento in gebruik is. Vervolgens begint de Undo/Redo functionaliteit. Het laatste moment wordt van de stapel gehaald en wordt hersteld. Het moment dat wordt gecre¨eerd hierdoor, wordt vervolgens op de Redostapel gezet.
14
Hoofdstuk 5
Tabbladen In de DIAS-applicatie zijn tabbladen een onderdeel van het paneel. Het zijn voor de gebruikers geen controls zoals in Visual Studio. Er waren bijgevolg beperkingen op de tabbladen. De tabbladen konden niet verticaal geplaatst worden en de header kon op de titel na ook niet veranderd worden. Er was ook geen mogelijkheid om in een tabblad een paneel en een kindtab te plaatsen. De tabbladen zijn niet de controls die standaard in de Windowsbibliotheek zitten. C&C maakt gebruik van DevExpress. Dit is een bibliotheek met controls die uitgebreid en aangepast zijn met meer mogelijkheden voor de programmeur en sneller en gebruiksvriendelijker voor de gebruiker. Hierdoor verloopt het programmeren anders dan met de Windowsbibliotheek. Hieronder beschrijven we de aanpassingen die ge¨ımplementeerd werden in het kader van dit eindwerk.
5.1
Ori¨ entatie
Om de ori¨entatie van de headers te veranderen, is er een veld bij UITabFolder geplaatst. Dit veld bevat de waarde HORIZONTAL of VERTICAL. Om een tabcontrol verticaal te plaatsen in Windows, wordt er gebruik gemaakt van de Alignment property. Voor de DevExpress-control is dit iets ingewikkelder. Windows t h i s . t a b C o n t r o l 1 . Alignment = System . Windows . Forms . TabAlignment . L e f t ;
DevExpress DiASTabControl t c = new DiASTabControl ( ) ; t c . H e a d e r L o c a t i o n = DevExpress . XtraTab . TabHeaderLocation . L e f t ; tc . HeaderOrientation = DevExpress . XtraTab . T a b O r i e n t a t i o n . V e r t i c a l ;
Om te beginnen is er HeaderLocation. Deze variabele zorgt ervoor dat de headers zich aan de linkse kant van de tabfolder bevinden. HeaderOrientation zorgt er dan weer voor dat de tekst verticaal staat. DiasTabControl is een klasse afgeleid van DevExpress.XtraTab.XtraTabControl. Hierdoor kunnen er later nog uitbreidingen gemaakt worden.
15
5.2
Font & Kleur
Om kleur en/of font van de header te veranderen kan er via een rechtermuisklik een propertiesmenu geopend worden (frmTabPageProp). Hierin kan de gebruiker via een FontDialog en ColorDialog de properties veranderen. Windows private void ChangeFontAndColor ( DiASTabPage tab , frmTabPageProp frmAT , UITabFolder u i t f ) { tab . Font = frmAT . HeaderFont ; u i t f . Font = frmAT . HeaderFont ; tab . F o r e C o l o r = frmAT . H e a d e r F o r e C o l o r ; u i t f . T i t l e C o l o r = frmAT . H e a d e r F o r e C o l o r ; tab . BackColor = frmAT . HeaderBackColor ; u i t f . H ea d e r Co l o r = frmAT . HeaderBackColor ; }
DevExpress private void ChangeFontAndColor ( DiASTabPage tab , frmTabPageProp frmAT , UITabFolder u i t f ) { tab . Appearance . Header . Font = frmAT . HeaderFont ; u i t f . Font = frmAT . HeaderFont ; tab . Appearance . Header . F o r e C o l o r = frmAT . H e a d e r F o r e C o l o r ; u i t f . T i t l e C o l o r = frmAT . H e a d e r F o r e C o l o r ; tab . Appearance . Header . BackColor = frmAT . HeaderBackColor ; u i t f . H ea d e r Co l o r = frmAT . HeaderBackColor ; }
Een verschil in de DevExpress-control is dat alle properties, die met uiterlijk te maken hebben, zich bevinden onder Appearance. De waarde wordt zowel in tab als in uitf opgeslaan. Dit komt doordat uitf hetgene is dat in de databank wordt opgeslaan en tab hetgene is dat gezien wordt en waar de events op kunnen gebeuren.
5.3
Kindtab
Een van de grootste problemen was dat er geen controls en tabbladen tesamen in een tabblad konden worden geplaatst. Dit probleem is opgelost door gebruik te maken van een Panel en Splitter. Een Splitter is een control die gebruikt wordt om de grootte te veranderen van twee andere, naast elkaar gedockte, controls. In dit geval is dit tussen het geplaatste Panel en de tabcontrol.
DiASTabPage p = ( ( ( System . Windows . Forms . ContextMenuStrip ) ( ( ( System . Windows . Forms . T o o l S t r i p I t e m ) ( s e n d e r ) ) . as DiASTabControl ) . S e l e c t e d T a b P a g e as DiASTabPage ; i f ( p == n u l l ) return ; System . Windows . Forms . Panel TopHeader = makePanel ( ”VERTICAL” , ” TopHeader ” + A a n t a l P a n e l e n ) ; System . Windows . Forms . S p l i t t e r s p l i t t e r = new System . Windows . Forms . S p l i t t e r ( ) ; s p l i t t e r . BackColor = System . Drawing . C o l o r . White ;
16
s p l i t t e r . Name = ” s p l i t t e r 1 ” ; s p l i t t e r . TabStop = f a l s e ; s p l i t t e r . Dock = D o c k S t y l e . L e f t ; DiASTabControl t c = new DiASTabControl ( ) ; t c . PanelName = TopHeader . Name ; t c . H e i g h t P a n e l = TopHeader . Width ; t c . Name = ”TABCTRLFOR ” + p . Name ; t c . H e a d e r L o c a t i o n = DevExpress . XtraTab . TabHeaderLocation . L e f t ; t c . H e a d e r O r i e n t a t i o n = DevExpress . XtraTab . T a b O r i e n t a t i o n . V e r t i c a l ; t c . Dock = D o c k S t y l e . F i l l ; t c . ContextMenuStrip = t h i s . contextMenuStripTab ; t c . Images = t h i s . i m a g e L i s t 1 ; p . C o n t r o l s . Add( t c ) ; p . C o n t r o l s . Add( s p l i t t e r ) ; p . C o n t r o l s . Add( TopHeader ) ;
De panelen krijgen de naam ”TopHeaderX”. X is het aantal panelen die al geplaatst zijn. De naam moet uniek zijn, omdat de geplaatste controls in het paneel een correcte parentname moeten hebben. Indien de naam niet uniek is, weet het programma niet in welke container het de controls moet plaatsen. De extra variabelen die nodig zijn om dit te kunnen opslaan zijn de naam en de hoogte of breedte van het paneel. Aangezien vroegere layouts gemaakt zijn zonder de optie om een paneel toe te voegen, moet er nog steeds een mogelijkheid zijn deze panelen te verstoppen. Om dit mogelijk te maken is er nog een variabele toegevoegd aan de UITabFolder.
17
Hoofdstuk 6
Vormen Om de layouts duidelijker en properder te maken, is het nodig om vormen en figuren toe te voegen. De rechthoek was al aanwezig, maar in veel omstandigheden is een lijn of een ovaal gewenst. Om deze vormen op een gemakkelijke manier toe te voegen is er gebruik gemaakt van Microsoft Power Packs. Deze bibliotheek bevat controls voor het toevoegen van lijnen en vormen. Er zijn twee pogingen gedaan om deze vormen toe te voegen.
6.1
Eerste poging
Figuur 6.1: Deel van de werkbalk om vormen toe te voegen In een eerste poging om vormen toe te voegen werd gekozen om shapes via de werkbalk te selecteren en zo op de layout te tekenen. Zoals op figuur 6.1 te zien is, werd er gebruik gemaakt van drie radiobuttons. Indien er een werd geselecteerd, werd de multiselect uitgeschakeld en in plaats daarvan kon de geselecteerde figuur getekend worden. Voor er werd getekend kon ook de kleur en dikte van de omtrek veranderd worden. Elke vorm heeft zijn eigen klasse, de klassen waren zelf afgeleid uit 2 klassen: • LineShape = Lijn • SimpleShape RectangleShape = Rechthoek OvalShape = Ovaal of Ellips Deze shapes zijn niet afgeleid van System.Windows.Forms.Control. Hierdoor kan je deze shapes niet rechtsreeks op de layout tekenen. Er moet dus nog een soort wrapper tussenkomen. Deze wrapper is een ShapeContainer. De ShapeContainer is wel afgeleid van Control en kan dus op de layout geplaatst worden. 18
Problemen Door het feit dat de vormen niet afgeleid zijn van Control, waren er een paar problemen omdat deze figuren ’at runtime’ geplaatst moeten kunnen worden. Om te beginnen kon de klasse UIControl van DIAS zelf niet gebruikt worden. Hierdoor werkten veel van de basisopdrachten die de controls al hadden niet op de vormen. Om deze problemen op te lossen moesten vele events van de controls aangepast en toegevoegd worden op de ShapeContainer. Dit leidde dan weer tot veel licht verschillende dubbele code. Een ander probleem was enkel zichtbaar wanneer er met tabbladen werd gewerkt. Doordat de shapes op ShapeContainers moeten geplaatst worden, moet er dus voor elk tabblad ook een ShapeContainer zijn. Op zichzelf is dit geen probleem, maar deze vormen moeten verplaatsbaar zijn. Doordat de vormen niet 100 procent werken als controls was het veranderen van parent een waarschijnlijk mogelijk, maar onnodig lastig karwei. Na bespreking met de externe promotor en begeleiders is ervoor gekozen om deze manier te laten vallen. Mede omdat alles erg onoverzichtelijk en overladen dreigde te worden.
6.2
Tweede poging
Om alle code zo proper mogelijk te houden is de beslissing genomen om de vormen als componenten toe te voegen. Dit had als nadeel dat de vormen moeilijker te vinden waren (opgelost door een andere uitbreiding, zie 12.1). Een ander nadeel was dat het niet meer vanzelfsprekend was om de richtingscoeffici¨ent van een lijn te laten veranderen door te slepen. Dit werd uiteindelijk vervangen door 2 componenten, een horizontale en verticale lijn. Aangezien het in offici¨ele documenten niet nodig is om schuine lijnen te hebben is dit niet verder meer bekeken. Hier is voorbeeldcode van de verticale lijn. public p a r t i a l c l a s s c t r l L i n e V e r t i c a l : Panel { private LineShape l i n e ; private S h a p e C o n t a i n e r s c ; public c t r l L i n e V e r t i c a l ( ) { InitializeComponent () ; Height = 200; Width = 3 0 ; s c = new S h a p e C o n t a i n e r ( ) ; s c . Parent = t h i s ; l i n e = new LineShape ( ) ; l i n e . Parent = s c ; l i n e . S t a r t P o i n t = new P o i n t ( Width / 2 , 0 ) ; l i n e . EndPoint = new P o i n t ( Width / 2 , H e i g h t ) ; s c . Shapes . Add( l i n e ) ; s c . SendToBack ( ) ; t h i s . SizeChanged += new EventHandler ( r e s i z e ) ; }
19
private void c t r l L i n e V e r t i c a l V i s i b l e C h a n g e d ( object s e n d e r , EventArgs e ) { t h i s . SendToBack ( ) ; s c . SendToBack ( ) ; }
private void r e s i z e ( object s e n d e r , EventArgs e ) { Panel t = ( Panel ) s e n d e r ; l i n e . S t a r t P o i n t = new P o i n t ( Width / 2 , 0 ) ; l i n e . EndPoint = new P o i n t ( Width / 2 , H e i g h t ) ; Width = 3 0 ; } protected override void OnPaint ( PaintEventArgs e ) { base . OnPaint ( e ) ; t h i s . SendToBack ( ) ; Color borderColor = this . ForeColor ; BackColor = C o l o r . T r a n s p a r e n t ; l i n e . BorderColor = borderColor ; }
De functie SendToBack wordt gebruikt zodat de vorm altijd op de achtergrond geplaatst wordt. Mocht dit niet het geval zijn, dan zou je de componenten die in de vorm staan niet meer kunnen bereiken.
Problemen Een probleem bij deze oplossing was het instellen van de achtergrondkleur van de ovaal. Aangezien de component afgeleid is van Panel zal de property Background de gehele achtergrond veranderen, niet enkel de ovaal. Een eerste poging om dit op te lossen was de property Background overschrijven. Dit leek allemaal goed in theorie, maar in praktijk was dit niet haalbaar. Dit komt doordat de property heel veel wordt opgevraagd achter de schermen. Bij het verslepen van de control werd telkens weer het hele paneel ingekleurd. Uiteindelijk is de beslissing gevallen om een extra property toe te voegen aan de figuur. Dit wordt gedaan aan de hand van de DiasProperty klasse. public DIASBase . B a s e O b j e c t s . D I A S P r o p e r t i e s P r o p e r t i e s { get { i f ( p r o p s == n u l l ) LoadProperties (0) ; return p r o p s ; } } public void L o a d P r o p e r t i e s ( decimal d ) { i f ( p r o p s == n u l l | | p r o p s . OwningObject == 0 ) { p r o p s = new C t r l O v a l P r o p e r t i e s ( d ) ; p r o p s . ValueChanged += new ChangedEventHandler ( p r o p s V a l u e C h a n g e d ) ; p r o p s . GetFromDB ( ) ; i f ( p r o p s . OwningObject > 0 )
20
{ setColor () ; } } p r o p s . GetFromDB ( ) ; } private void p r o p s V a l u e C h a n g e d ( object s e n d e r , EventArgs e ) { setColor () ; } [ Serializable ] public c l a s s C t r l O v a l P r o p e r t i e s : D I A S P r o p e r t i e s { ∗∗∗ Kan d i t n i e t l a t e n z i e n }
Door een DiasProperties-object toe te voegen aan de component kan de property FillColor van OvalShape gebruikt worden. Het volgende probleem dat kwam opsteken was dat de wijziging van kleur pas zichtbaar was nadat er opgeslaan werd. Dit probleem werd opgelost door een event aan te maken. Een geserialiseerde klasse kan normaal geen events bevatten, maar hier is een omweg voor gebruikt. public delegate void ChangedEventHandler ( object s e n d e r , EventArgs e) ; [ NonSerialized ] private ChangedEventHandler c h a n g e d ; public event ChangedEventHandler ValueChanged { add { c h a n g e d += v a l u e ; } remove { c h a n g e d −= v a l u e ; } }
NonSerialized kan enkel op velden gebruikt worden. Daarom moeten we de handler apart declareren zodat er NonSerialized op kan toegepast worden.
21
Hoofdstuk 7
Subtypes Als er een nieuwe layout wordt gemaakt in de UID, moest er ´e´en type en ´e´en subtype gekozen worden. Bijvoorbeeld: Indien je een opvolglayout wilt maken bedoeld om telefoongesprekken te volgen, is het type opvolg en subtype telefoongesprek. Dit zorgde ervoor dat er voor elk subtype een aparte layout gemaakt moest worden. Het is mogelijk dat sommige van deze subtypes dezelfde layout gebruiken. Er is een kopieerfunctie voorzien, maar deze meer dan twee keer toepassen is eigenlijk teveel werk. Zeker als er later aanpassingen moeten gemaakt worden, is dit niet gewenst. Daarom is er een aanpassing gedaan zodat een gemaakte layout toepasbaar is voor verschillende subtypes.
7.1
Nieuwe Layout
Om te beginnen is de combobox vervangen door een lijst (zie figuur 7.1). In deze lijst bevinden zich alle subtypes voor het gekozen type. Indien er een nieuwe layout gemaakt wordt zonder een subtype te kiezen, wordt automatisch het subtype ’-standard-’ meegegeven. Aangezien de bruikbare controls voor verschillende subtypes vaak verschillend zijn, moeten deze aangepast worden. Enkel de controls die bruikbaar zijn voor alle mogelijke subtypes worden getoond.
7.2
Oude Layout
Er zijn een paar aanpassingen nodig in de GUI om een opgeslagen layout te openen. Er is een kolom ’Aantal’ toegevoegd. Hierin staat voor hoeveel subtypes deze layout gebruikt wordt. Onder ’Subtype’ worden deze subtypes uitgeschreven gescheiden door //. Er is ook de mogelijkheid om de subtypes te veranderen tijdens het maken van de layout. Hiervoor kan er via een knop in de taakbalk een venster geopend worden (zie figuur 7.3). In dit venster wordt de naam en het type van de layout getoond en kunnen in de lijst de subtypes veranderd worden.
22
Figuur 7.1: Een nieuwe layout maken
7.3
Databank
Om deze subtypes op te slaan zijn er enkele regels afgesproken. De subtypes worden opgeslaan in ´e´en lange string. De separator hiertussen werd door C&C bepaald en is (char)3. In C# worden de subtypes opgeslaan als decimals. Dit betekent dat deze geconverteerd moeten worden naar strings om ze op te slaan in de databank. Deze strings moeten in AMERICAN format staan. Dit wilt zeggen dat de komma een punt moet worden. Dit wordt gedaan met volgend commando. S t r i n g s u b t y p e=CommonFunctions . G e t O b j S t r i n g ( t ) ;
De conversie moet natuurlijk ook omgekeerd gebeuren. Dit wordt gedaan via; decimal s u b t y p e = decimal . P a r s e ( t , CommonFunctions . U S C u l t u r e I n f o ) ;
Natuurlijk moeten de oude subtypewaarden geconverteerd worden naar de subtypes tabel. Hiervoor is er een progress-script geschreven. FOR EACH UIObject WHERE SubTypes=” ” : ASSIGN SubTypes=o b j s t r i n g ( SubType obj ) . END.
Deze layouts moeten geladen worden in DIAS. Indien er bijvoorbeeld naar contacten gezocht wordt en er wordt voor een bepaald subtype gekozen, dan moet een layout getoond worden dat dit subtype bevat in zijn lijst. Dit was ´e´en van de eerste pogingen om een query te schrijven en er zijn een paar pogingen gedaan om in Progress een substring te zoeken in een lijst. Hier zijn enkele pogingen. 23
• SubTypes MATCHES *1* OR SubTypes MATCHES *0* • SubTypes CONTAINS 1 OR SubTypes CONTAINS 0 De eerste poging was relatief correct, maar er waren complicaties. Deze kreeg nog steeds niet het gewenste resultaat doordat de standaard layouts eerst werden getoond. Hierdoor zijn er een paar aanpassingen gedaan en dit is het eindresultaat. t h i s . Proxy . g e t u i o b j e c t ( s t r i n g . Format ( ”FOR EACH u i o b j e c t WHERE u i o b j e c t . objectName EQ \”{0}\” AND (STRING( SubTypes ) MATCHES ’ ∗ { 1 } ∗ ’ ) BY SubTypes DESCENDING” , objectName , CommonFunctions . G e t O b j S t r i n g ( s u b t y p e ) ) , 0 , true , out ds , out e r r n b , out errparam ) ; i f ( ds . TTUIObject . Count == 0 ) { t h i s . Proxy . g e t u i o b j e c t ( s t r i n g . Format ( ”FOR EACH u i o b j e c t WHERE u i o b j e c t . objectName EQ \”{0}\” AND (STRING( SubTypes ) MATCHES ’ ∗ 0 . 0 0 0 0 0 0 ∗ ’ ) BY SubType obj DESCENDING” , objectName ) , 0 , true , out ds , out e r r n b , out errparam ) ; }
De query is dus in 2 gesplitst en indien er geen layouts zijn met het gewenste subtype, wordt de standaard layout opgehaald. Vervolgens is 0 ook 0.000000 geworden. Dit komt door de omzetting van decimal naar string.
Figuur 7.2: Een layout openen
24
Figuur 7.3: Subtypes veranderen
25
Hoofdstuk 8
PropertyGrid Het bestaande propertygrid bevat zowel de events als de properties van de geselecteerde control(s). De events zijn scriptjes die zelf kunnen geschreven worden om bijvoorbeeld sommige controls te verstoppen zolang er niets is ingevuld. Er werd gevraagd om deze propertygrid op te splitsen in twee grids, ´e´en voor de properties en ´e´en voor de events. Om dit te realiseren moet er gebruik gemaakt worden van de BrowsableAttributes property van het grid. Het probleem is dat de standaard propertygrid maar ´e´en attribute toestaat, maar aangezien er meerdere zijn moet deze propertygrid uitgebreid worden. Er is ook van de gelegenheid gebruik gemaakt om niet alleen categorie¨en te gebruiken, maar ook properties zelf. 1
8.1
Wrapper
Om te beginnen wordt er een wrapperklasse geschreven. Deze klasse bevat een referentie naar het object dat moet getoond worden in het propertygrid. Bijkomend bevat het ook een referentie naar alle properties die moeten getoond worden. Standaard bevat deze lijst alle properties die getoond kunnen worden. Deze klasse implementeert de interface System.ComponentModel.ICustomTypeDescriptor. Dit is noodzakelijk omdat de functie TypeDescriptor.GetProperties() wordt gebruikt om de properties van het geselecteerde object op te vragen.
8.2
FilteredPropertyGrid
De normale propertygrid heeft enkel BrowsableAttributes. De nieuwe heeft er vier, namelijk: • BrowsableAttributes: overschreven van PropertyGrid. • HiddenAttributes: alle properties waarvan hun attributen zich bevinden in deze collectie zijn verstopt. • BrowsableProperties: alle properties in deze collectie worden getoond. • HiddenProperties: alle properties in deze collectie worden niet getoond. 1 Deze
code is gebaseerd op [5]
26
Er worden drie functies overschreven. Zoals hierboven vernoemd, wordt BrowsableAttributes overschreven. Ook SelectedObject en SelectedObjects moeten overschreven worden. Deze moeten herschreven worden omdat ze moeten rekening houden met de wrapperklasse. Hier is code van hoe de properties worden opgevuld: private void S h o w A t t r i b u t e ( A t t r i b u t e a t t r i b u t e ) { i f ( m Wrappers [ 0 ] != n u l l ) { i f ( m Wrappers [ 0 ] . S e l e c t e d O b j e c t != n u l l ) { PropertyDescriptorCollection filteredoriginalpropertydescriptors = T y p e D e s c r i p t o r . G e t P r o p e r t i e s ( m Wrappers [ 0 ] . S e l e c t e d O b j e c t , new A t t r i b u t e [ ] { a t t r i b u t e } ) ; i f ( f i l t e r e d o r i g i n a l p r o p e r t y d e s c r i p t o r s == n u l l | | f i l t e r e d o r i g i n a l p r o p e r t y d e s c r i p t o r s . Count == 0 ) throw new ArgumentException ( ” A t t r i b u t e not found ” , a t t r i b u t e . ToString ( ) ) ; foreach ( P r o p e r t y D e s c r i p t o r p r o p e r t y d e s c r i p t o r in filteredoriginalpropertydescriptors ) ShowProperty ( p r o p e r t y d e s c r i p t o r ) ; } } } private void ShowProperty ( P r o p e r t y D e s c r i p t o r p r o p e r t y ) { i f ( ! m PropertyDescriptors . Contains ( property ) ) m P r o p e r t y D e s c r i p t o r s . Add( p r o p e r t y ) ; }
Telkens ´e´en van de vier properties verandert, wordt de private functie RefreshProperties() opgeroepen. Deze bouwt een lijst met alle properties die moeten getoond worden en geeft die door aan de wrapper. Deze wrapper is verbonden met het propertygrid.
27
Hoofdstuk 9
Versiebeheer Voor het uitwerken van dit onderdeel van deze masterproef, werden aanpassingen aan een layout als zeer storend ervaren. De aangepaste layout kon namelijk niet uitgecheckt worden.Dit wil zeggen dat aanpassingen meteen zichtbaar waren van zodra er op save geklikt werd. Bijgevolg zag een collega, die deze layout op dat moment gebruikte, controls verdwijnen of tevoorschijn komen. Om dit op te lossen, wordt er aan versiebeheer gedaan.
9.1
Analyse
Versiebeheer is geen eenvoudige feature, daarom is er eerst een analyse uitgevoerd. Met een gebrek aan voorkennis en werkwijze van het bedrijf werd de eerste analyse afgewerkt (te vinden in Bijlage). In deze analyse werden velden toegevoegd aan reeds bestaande databanktabellen. Hierdoor werd er tijd bespaard door minder databankwerk. Tijdens de bespreking is er veel van gedachten gewisseld over de manier waarop er te werk wordt gegaan bij C&C en is er een nieuwe analyse gemaakt(ook te vinden in Bijlage). Er komt een nieuwe databanktabel bij. Er komen dus ook nieuwe procedures en nieuwe klassen in C#.
9.2
Databank
Om te beginnen moet de layout van de databank aangepast worden. In de oude layout heeft elk object precies ´e´en layout. In de bespreking is er bepaald om een extra tabel toe te voegen. Deze tabel UILayoutVersion bevat het layoutversienummer en de foreign keys (relaties) met de controls, tabbladen en settings. De tabel bevat ook of de versie is ingecheckt. Vroeger waren de foreign keys van UILayout. Deze zijn dus verhuisd. Er is een veld toegevoegd aan UILayout, namelijk testusers. Dit is een lijst met gebruikers die altijd de laatste versie van een layout zullen zien. Deze gebruikers zullen dus de incheck-variabele negeren en altijd de laatste versie zien. Aangezien er een nieuwe tabel is, moeten er een paar zaken veranderen. Om te beginnen zijn er de indexen. Bijvoorbeeld: in de oude layout was in een UITabfolder zijn Name en zijn uilayoutobj uniek. Dit is niet meer het geval. Dus moet deze aangepast worden. Een tweede zaak zijn de triggers. Deze worden 28
Figuur 9.1: Relaties nieuwe databank gebruikt om alles properder en makkelijker te cre¨eren en op te kuisen. Hier is een voorbeeld van een paar Delete-triggers. TRIGGER PROCEDURE FOR DELETE OF UILayoutVersion . /∗ d e l e t e s e t t i n g s ∗/ FOR EACH U I S e t t i n g WHERE U I S e t t i n g . U I L a y o u t V e r s i o n o b j EQ UILayoutVersion . U I L a y o u t V e r s i o n o b j EXCLUSIVE : DELETE U I S e t t i n g . END. /∗ delete tabfolders ∗/ ... /∗ d e l e t e components ∗/ ... /∗ delete object ∗/ ... { lib / record del . i &TABLE=UILayoutVersion &Obj=UILayoutVersion . U I L a y o u t V e r s i o n o b j }
TRIGGER PROCEDURE FOR DELETE OF UILayout . /∗ d e l e t e s e t t i n g s ∗/ FOR EACH UILayoutVersion WHERE UILayoutVersion . U I L a y o u t o b j EQ UILayout . U I L a y o u t o b j EXCLUSIVE : DELETE UILayoutVersion . END.
{ lib / record del . i
&TABLE=UILayout &Obj=UILayout . U I L a y o u t o b j }
Tot slot moeten ook nog de procedures aangepast worden. Deze worden gebruikt om data uit de databank te halen en er ook in op te slaan. Dit wordt gedaan door middel van datasets. Het eerste dat gedaan moet worden zijn de datasetdefinities veranderen. Hier is een voorbeeld van de definitie van de nieuwe UILayout (alle volgende voorbeelden zullen van UIlayout zijn. UILayoutVersion wordt niet getoond omdat er meer velden zijn en het heeft connecties met te veel andere tabellen).
29
DEFINE TEMP−TABLE TTLayout NO−UNDO LIKE UILayout . DEFINE TEMP−TABLE TTLayoutVersion NO−UNDO LIKE UILayoutVersion .
DEFINE DATASET dsUILayout FOR TTLayout , TTLayoutVersion DATA−RELATION r e l v e r s i o n FOR TTLayout , TTLayoutVersion RELATION−FIELDS ( UILayout obj , U I L a y o u t o b j )
De dataset van UILayoutversion is natuurlijk groter omdat daar ook de controls, tabfolders en settings aan gekoppeld moeten worden. Nadat alle datasetdefinities geschreven zijn, moeten de procedures aangepast worden. Er zijn drie soorten procedures. • Dataset ophalen met behulp van ID • Dataset ophalen met behulp van query • Dataset in databank opslaan
Dataset ophalen met behulp van ID Hier staat niet alle code afgeprint, om de leesbaarheid te bevorderen. { lib / def dsuilayout . i } DEFINE INPUT PARAMETER d U I L a y o u t o b j NO−UNDO LIKE UILayout . U I L a y o u t o b j . DEFINE OUTPUT PARAMETER DATASET FOR dsUILayout . DEFINE OUTPUT PARAMETER i E r r n b AS INTEGER NO−UNDO. DEFINE OUTPUT PARAMETER szErrParam AS CHARACTER NO−UNDO. ... ASSIGN s z P r e p a r e = SUBSTITUTE( ”FOR EACH UILayout WHERE UILayout . U I L a y o u t o b j EQ &1 NO−LOCK” ,STRING( d U I L a y ou t o b j ) ) . CREATE QUERY pQuery . pQuery : SET−BUFFERS(BUFFER UILayout :HANDLE) . ASSIGN fOK = pQuery :QUERY−PREPARE( s z p r e p a r e ) NO−ERROR. /∗ e r r o r i n v u l l e n ∗/ CREATE DATA−SOURCE p d s l a y o u t . ASSIGN p d s l a y o u t :QUERY = pQuery . DEFINE DATA−SOURCE d s V e r s i o n FOR UILayoutVersion . BUFFER TTLayout : h a n d l e :ATTACH−DATA−SOURCE( p d s l a y o u t , ? , ? , ? ) . BUFFER TTLayoutVersion : h a n d l e :ATTACH−DATA−SOURCE(DATA−SOURCE d s V e r s i o n :HANDLE, ? , ? , ? ) . ASSIGN fOK = DATASET dsUILayout : FILL ( ) NO−ERROR. /∗ e r r o r i n v u l l e n ∗/ DELETE OBJECT pQuery . DELETE OBJECT p d s l a y o u t .
Om te beginnen wordt de datasetdefinitie ge¨ıncludeerd. Vervolgens gaat men de in- en uitvoerparamaters declareren. Hierna wordt de query gemaakt en wordt de foutopvang gedaan. Ten laatste wordt de data in de dataset gestoken en teruggegeven.
30
Dit is de meest gebruikte manier om zaken uit te databank te halen. Het wordt gebruikt als je via een ID ´e´en bepaald object uit de databank wilt halen, bijvoorbeeld als je een gekozen layoutversie wilt openen.
Dataset ophalen met behulp van query { lib / def dsuilayout . i } DEFINE DEFINE DEFINE DEFINE DEFINE DEFINE
INPUT INPUT INPUT OUTPUT OUTPUT OUTPUT
PARAMETER PARAMETER PARAMETER PARAMETER PARAMETER PARAMETER
s z P r e p a r e AS CHARACTER NO−UNDO. iNumRec AS INTEGER NO−UNDO. fNoLobs AS LOGICAL NO−UNDO. DATASET FOR dsUILayout . iErrNb AS INTEGER NO−UNDO. szErrParam AS CHARACTER NO−UNDO. NO−UNDO.
DEFINE VARIABLE fOK AS LOGICAL
RUN common/ g e t r e c o r d . p (INPUT ” UILayout ” : U, INPUT s z P r e p a r e , INPUT iNumRec , INPUT fNoLobs , OUTPUT TABLE TTLayout , OUTPUT iErrNb , OUTPUT szErrParam ) . IF iErrNb NE 0 THEN RETURN. FOR EACH TTLayout NO−LOCK: /∗ t o e v o e g e n S e c u r i t y −r e c o r d s ∗/ FOR EACH UILayoutVersion WHERE UILayoutVersion . U I L a y o u t o b j EQ TTLayout . U I L a y o u t o b j NO−LOCK: CREATE TTLayoutVersion . BUFFER−COPY UILayoutVersion TO TTLayoutVersion . END. END.
Ook hier wordt eerst de definitie ge¨ıncludeerd en vervolgens de in- en uitvoerparameters gedeclareerd. Het grote verschil hier is dat de query een invoerparameter is. Deze wordt dan via get record-methode gebruikt om de data op te halen. Vervolgens wordt de dataset opgevuld met de correcte gegevens. Deze methode wordt gebruikt indien je meerdere objecten wilt ophalen of ´e´en object niet op basis van zijn ID. Een voorbeeld hiervan is het weergeven van de lijst met layouts.
Dataset in databank opslaan Hier staat niet alle code afgeprint, om de leesbaarheid te bevorderen. { lib / def dsuilayout . i } DEFINE INPUT PARAMETER d U s e r o b j AS DECIMAL NO−UNDO. DEFINE INPUT−OUTPUT PARAMETER DATASET FOR dsUILayout . ... FIND FIRST TTLayout EXCLUSIVE NO−ERROR. IF NOT AVAIL( TTLayout ) THEN DO: ASSIGN i E r r n b = 2 0 0 7 . RETURN.
31
END. FIND FIRST UILayout WHERE UILayout . U I L a y o u t o b j EQ TTLayout . U I L a y o u t o b j EXCLUSIVE NO−ERROR. IF NOT AVAIL( UILayout ) THEN CREATE UILayout . BUFFER−COPY TTLayout EXCEPT U I L a y o u t o b j TO UILayout . u p d a t e L a y o u t V e r s i o n ∗/
/∗
FOR EACH TTLayoutVersion WHERE TTLayoutVersion . U I L a y o u t o b j EQ TTLayout . U I L a y o u t o b j EXCLUSIVE BY u i l a y o u t v e r s i o n o b j DESC : FIND FIRST UILayoutVersion WHERE UILayoutVersion . U I L a y o u t V e r s i o n o b j EQ TTLayoutVersion . U I L a y o u t V e r s i o n o b j EXCLUSIVE NO−ERROR. IF AVAIL u i l a y o u t v e r s i o n THEN DO: ... ELSE DO : CREATE u i l a y o u t v e r s i o n . BUFFER−COPY TTLayoutVersion EXCEPT U I L a y o u t V e r s i o n o b j U I L a y o u t o b j TO UILayoutVersion . uilayoutversion . uiLayout obj = uilayout . uilayout obj . BUFFER−COPY UILayoutVersion TO TTLayoutVersion . /∗ u p d a t e temp−t a b l e ∗/ END. END.
ASSIGN TTLayout . U I L a y o u t o b j = UILayout . U I L a y o u t o b j . /∗ d a t a s e t ∗/
update
Het begin is hier weer hetzelfde. Vervolgens wordt er gecheckt of de tabelrij al bestaat. Indien niet wordt er een nieuwe gecre¨eerd. Hierna wordt de dataset in deze rij gekopieerd. Een laatste iets dat moet gebeuren is de al bestaande rijen een correcte id geven. Om dit vlot te laten gebeuren is er besloten om de UILayout obj van de reeks bestaande rijen te kopi¨eren naar UILayoutVersion obj.
9.3
Aanpassingen UID
Om deze databankwijziging te kunnen gebruiken in de UID, moeten er een paar zaken veranderen. Er is een nieuwe klasse UILayoutVersion gecre¨eerd die veel functionaliteit van de vroegere UILayout overneemt. Anderzijds moet bijna overal waar vroeger UILayout voor werd gebruikt, aangepast worden zodat dit UILayoutVersion wordt. Grotere wijzigingen worden hieronder besproken.
9.4
Openen
Aangezien een UIObject nu verbonden is met een UILayoutVersion en niet meer met een UILayout, moet er in frmUIObjects ook een verandering gebeuren. Er is nu een object voor elke versie van een bepaalde layout. Indien deze zouden
32
worden gebruikt om het menu te vullen, zouden er enorm veel mogelijkheden zijn. Dit is niet overzichtelijk. Daarom is besloten om de layouts te gebruiken in plaats van de objecten. Er is maar ´e´en probleem hiermee. De data in de layouts is niet uniek genoeg om een layout te herkennen. Hierdoor moet er de mogelijkheid zijn om aan data te geraken van de laatste versie die gemaakt is. Het is hier dat er een probleem is ontstaan. Bij de eerste poging werd er bij elke opgevraagde layout een functie getLatestVersion opgeroepen. public L i s t g e t V e r s i o n s ( ) { L i s t v e r s i o n s = new L i s t () ; dsUILayoutVersionDataSet dsdata = null ; int errnb = 0 ; s t r i n g errparam = ” ” ; DIASMan . I n s t a n c e . Proxy . g e t u i l a y o u t v e r s i o n s ( s t r i n g . Format ( ”FOR EACH UILayoutVersion WHERE UILayoutVersion . U I L a y o u t o b j EQ {0} ” , CommonFunctions . G e t O b j S t r i n g ( o b j ) ) , 0 , true , out dsdata , out e r r n b , out errparam ) ; i f ( e r r n b != 0 ) throw new E x c e p t i o n (DIASMan . I n s t a n c e . FormatErrorMessage ( e r r n b , errparam ) ) ; foreach ( d s U I L a y o u t V e r s i o n D a t a S e t . TTLayoutVersionRow row in d s d a t a . TTLayoutVersion ) { v e r s i o n s . Add(new UILayoutVersion ( row ) ) ; } v e r s i o n s = v e r s i o n s . OrderByDescending ( x => x . Major ) . ThenByDescending ( x => x . Minor ) . ToList() ; i f ( d s d a t a != n u l l ) { CommonFunctions . C l e a r D a t a S e t ( d s d a t a ) ; dsdata . Dispose ( ) ; dsdata = null ; } return v e r s i o n s ; } public UILayoutVersion g e t L a s t V e r s i o n ( ) { L i s t v e r s i o n s = g e t V e r s i o n s ( ) ; return v e r s i o n s [ 0 ] ; }
In deze functie halen we eerst alle versies op en vervolgens geven we degene met het hoogste versienummer terug. In de getVersions-methode gebruiken we de procedure met query omdat de layoutversions worden opgehaald met behulp van de uilayout obj en niet met uilayoutversion obj. Daarna worden alle versies in een lijst gestoken en wordt deze met behulp van Linq[6] gesorteerd op het versienummer. Het grote probleem bij deze poging was dat voor elke layout telkens weer een call naar de databank moet gedaan worden. Hierdoor onstaat er een groot performantieprobleem. Deze methode is dus niet bruikbaar.
33
Hierna is besloten om een soort lightklassen te maken. Deze klassen zouden de data bevatten van de objecten, versies en layouts, maar geen logica. Deze zouden gevuld worden met een dataset en bijhorende procedures, waarbij alle gegevens voor elke layout in ´e´en call worden opgevraagd. Hierdoor verbeteren we de performantie en houden enkel de noodzakelijke gegevens bij om geheugen te besparen. We vullen nu frmUIObjects op met de LightUILayout en maken hier gebruik van de lijst met versies die op voorhand zijn opgeslagen om zonder databankcalls de laatste versie te krijgen. Nadat de layout is gekozen, wordt er een nieuw venster geopend. Dit venster lijkt op frmUIObjects, maar in plaats van LightUILayouts worden hier UILightLayoutVersions getoond. Als er een versie is gekozen om aan te passen, wordt er via de UILayoutVersion obj, die werd opgeslaan in de lightklasse, een call gedaan naar de databank om het correcte object op te halen.
9.5
Problemen
Er waren een paar problemen met allemaal dezelfde oorzaak. De problemen waren het verdwijnen van tabfolders en componenten na het opslaan, het niet compleet verwijderen van objecten in de database, etc. Al deze problemen hadden hun oorsprong in het niet veranderen van de triggers en indexen. Aangezien dit enkel in theorie werd gezien tijdens de lessen databanken, werd dit vergeten. Hierdoor waren de delete-triggers niet in orde, werd er geen unieke id aan een nieuwe versie gegeven en waren sommige zaken uniek die niet meer uniek mochten zijn. Een probleem dat losstond van het vorige stelde zich bij het ophalen van een gemaakte layout in DIAS. Subtypes zijn door een vorige uitbreiding aanpasbaar na het maken van een object. Nu er ook versies zijn, wil dit zeggen dat een vorige versie meer subtypes kan bevatten dan een nieuwere versie. Aangezien bij het laden van layouts alle objecten worden opgehaald met het correcte subtype en daar de laatst ingecheckte versie wordt getoond, werd er soms een layout getoond die niet meer het correcte subtype bevatte. Dit is opgelost door de gekozen recente layout te checken en indien deze niet het juiste subtype heeft, wordt hij op een blacklist gezet en nemen we de volgende layout.
9.6
Testgebruikers
Om een nieuwe versie en testgebruikers toe te voegen, maakt men gebruik van onderstaand menu. De testgebruikers worden in de databank opgeslagen als een lange string van hun ID’s met (char)3 als separator. Deze ID’s worden gebruikt om de bovenstaande lijst te vullen. Elk item van de lijst heeft als tag zijn ID. Indien men een nieuwe testgebruiker wilt toevoegen, moet men in de textbox boven de lijst diens naam typen. Deze textbox bevat autocompletion. Hierdoor kan men een naam terugvinden, ook als hem niet volledig gekend is. public void AutoComplete ( ) { AutoItem = new A u t o C o m p l e t e S t r i n g C o l l e c t i o n ( ) ; foreach ( User u in DIASMan . I n s t a n c e . Cache . U s e r s . V a l u e s )
34
{ AutoItem . Add( u . Name) ; } txbAddUser . AutoCompleteCustomSource = AutoItem ; }
Figuur 9.2: Menu voor versiebeheer
35
Hoofdstuk 10
Scrolling Sommige klanten willen in plaats van een formulier met tabs, alles op ´e´en groot blad. Om dit mogelijk te maken moet het in de UID mogelijk zijn om te scrollen terwijl je de controls verplaatst.
10.1
Probleem
De eerste oplossing die geprobeerd werd, was het gebruiken van de autoscrollproperty van het paneel waar de controls zich op bevinden. Dit werkte indien je de controls verplaatste door drag & drop, maar wanneer je de control wou verplaatsen via zijn properties werkte dit niet. Het probleem was dat de autoscrollposition niet werd meegerekend. Pogingen om dit op te lossen zijn allemaal mislukt en daarom is er overgegaan op een andere optie.
10.2
Oplossing
De autoscrollproperty van het paneel is afgezet en de property is aangezet op de tabpagina waarin het paneel zich bevindt. Dit zorgt ervoor dat de autoscrollpositie verdwijnt. De positie op het paneel is nu vast en de autoscrollpositie die normaal problemen geeft voor het paneel vallen weg omdat het paneel gedockt is in de tabpagina. Om automatisch te kunnen scrollen, wordt er gekeken of de linkermuisknop is ingeklikt en waar zijn positie is. Als zijn positie bovenaan het scherm is, wordt er naar boven gescrolled en als het zich onderaan bevindt, is het naar beneden. Natuurlijk moet er ook rekening gehouden worden met de grootte van het paneel. Indien de scrollbalk helemaal naar beneden is gescrold moet het paneel vergroten. Dit lijkt makkelijker dan het is, want hoe wordt er gecheckt waar de scrollbalk is? Om dit te doen is er gebruik gemaakt van Win32 [7]. [ DllImport ( ” user32 ” ) ] private s t a t i c extern i n t G e t S c r o l l I n f o ( I n t P t r hwnd , i n t nBar , r e f SCROLLINFO s c r o l l I n f o ) ; [ DllImport ( ” user32 . d l l ” ) ] public s t a t i c extern void m o u s e e v e n t ( i n t dwFlags , i n t dx , i n t dy , i n t dwData , i n t dwExtraInfo ) ;
36
// t h i s w i l l c a u s e a v e r t i c a l s c r o l l // h e i g h t >0 = naar onder public s t a t i c void V S c r o l l ( i n t h e i g h t ) { m o u s e e v e n t ( 0 x0800 , 0 , 0 , h e i g h t , 0 ) ; } public struct SCROLLINFO { public i n t c b S i z e ; public i n t fMask ; public i n t min ; public i n t max ; public i n t nPage ; public i n t nPos ; public i n t nTrackPos ; } // g e t i f s c r o l l b a r i s down public s t a t i c bool ReachedBottom ( S c r o l l a b l e C o n t r o l pan ) { SCROLLINFO s c r o l l I n f o = new SCROLLINFO( ) ; s c r o l l I n f o . c b S i z e = Marshal . S i z e O f ( s c r o l l I n f o ) ; s c r o l l I n f o . fMask = 0 x10 | 0 x1 | 0 x2 ; G e t S c r o l l I n f o ( pan . Handle , 1 , r e f s c r o l l I n f o ) ; //nBar = 1 −> VScrollbar return ( s c r o l l I n f o . max == s c r o l l I n f o . nTrackPos + s c r o l l I n f o . nPage ) | | ( s c r o l l I n f o . max == s c r o l l I n f o . nTrackPos + s c r o l l I n f o . nPage − 1 ) ; }
De functie ReachBottom geeft true terug wanneer de scrollbalk helemaal beneden staat. De functie VScroll wordt gebruikt als zekerheid. De functie hoeft er niet gebruikt te worden, maar ze geeft zekerheid dat de scrollbalk meebeweegt met de muis. Om de hoogte van het paneel aan te passen,wordt volgende code gebruikt. i f ( p .Y > t h i s . H e i g h t − b o r d e r s e n s i t i v i t y ) { i f ( S c r o l l A i d . ReachedBottom ( p a n e l C o n t r o l ) ) p a n e l C o n t r o l . A u t o S c r o l l M i n S i z e = new S i z e ( p a n e l C o n t r o l . A u t o S c r o l l M i n S i z e . Width , panelControl . AutoScrollMinSize . Height + scrollsensitivity ) ; i f ( p a n e l C o n t r o l . V e r t i c a l S c r o l l . Value <= p a n e l C o n t r o l . V e r t i c a l S c r o l l . Maximum − scrollsensitivity ) p a n e l C o n t r o l . V e r t i c a l S c r o l l . Value += scrollsensitivity ; }
de snelheid of gevoeligheid van het scrollen kan aangepast worden in de taakbalk. De hoogte van het paneel is altijd zo hoog als de laagste control plus een bepaalde marge. Als de laagste control verwijderd wordt, wordt ook de hoogte opnieuw berekend.
37
Hoofdstuk 11
Conditioner Soms is het gewenst dat een control enkel zichtbaar is als een andere control is ingevuld of aangevinkt. Om zaken zoals dit te regelen is er de mogelijkheid tot scripting in de UID gestoken. Dit heeft wel een groot nadeel. Het is namelijk niet gebruiksvriendelijk. Mensen zonder een achtergrond in programmeren zullen het moeilijk hebben om dit te snappen en te gebruiken. Dit heeft tot gevolg dat het scriptingonderdeel van de layouts gevraagd wordt aan de medewerkers van C&C. Dit is tijd dat nuttiger kan worden gebruikt en dus verloren gaat. Daarom moet er een andere, gebruiksvriendelijkere oplossing komen. Dit leidt tot een conditioner. De conditioner is gebaseerd op een al bestaande conditioner ergens anders in de DIAS-software. Wegens tijdgebrek is deze conditioner niet volledig af. De reeds bestaande conditioner is volledig aangepast, maar het deel dat van de conditie het resultaat maakt, is nog niet ge¨ımplementeerd.
11.1
Voorbereiding
Om de conditioner te kunnen testen en snel implementeerbaar te maken, is ervoor gekozen om het enkel te implementeren voor de property IsMandatory. Deze property is een boolean, maar de conditie is een string, bijgevolg moet deze property worden aangepast. Er moet ook een GUI gemaakt worden zodat de conditie kan worden aangepast. Het is deze GUI die al deels bestaat in een ander onderdeel van DIAS. Om van deze GUI gebruik te kunnen maken, moet je in de propertygrid van de UID deze kunnen oproepen. Dit wordt op de volgende manier bereikt. [ Browsable ( true ) ] [ Category ( ”DIAS” ) ] [ E d i t o r ( typeof ( DIASBase . U I O b j e c t s . UID . C o n d i t i o n ) , typeof ( System . Drawing . D e s i g n . UITypeEditor ) ) ] public S t r i n g IsMandatory {∗∗∗}
Deze stukken code moeten boven de property-implementatie worden geplaatst, waar een conditie gewenst is. Browsable geeft aan of de property zichtbaar is.
38
In Category kan er een categorie genoemd worden onder welk de property zich zal bevinden. De editor geeft aan met welke klasse dat property kan aangepast worden. In dit geval is dit de klasse Condition. Deze klasse is afgeleid van de klasse UITypeEditor en moet twee methoden declareren. De eerste methode is GetEditStyle. Deze bevat de manier waarop het propertyveld eruit ziet in de grid. Er zijn drie mogelijkheden: • Niets (default) • Dropdown (geeft een dropdownlijst met mogelijke waarden) • Modal (mogelijkheid om venster te openen) Het is de derde waarde die nodig zal zijn. De tweede methode is EditValue. In deze methode wordt het venster geopend en wordt de waarde aan de property gegeven.
11.2
Conditioner & Conditie
Figuur 11.1: De originele conditionbuilder Aan de conditioner is niet veel veranderd, maar ´e´en van de verandering zorgt voor veel problemen. Er is namelijk een veld toegevoegd om een property van de controls te geven zoals checked, text, etc. Het veld toevoegen was niet zo moeilijk. De correcte properies bij de juiste controls weergeven was iets moeilijker, maar ging relatief vlot. Het is het omgekeerde dat lastig is en ook een reden waarom dit niet voltooid is. Hoe ga je zonder een hele if/else-structuur de property van een control ophalen uit een variabele die de naam heeft van de property. Zoals vernoemd is een if/else-structuur best mogelijk, maar indien je moet rekening houden met de aard van de control (checkbox, textbox, radiobutton,...) en de verschillende properties is dit niet overzichtelijk. Bij de vorige conditioner was er geen propertyveld en werd de conditie vertaald bij het ophalen van de data in de databank. Dit is hier niet mogelijk omdat de databank de waarden niet kent van vele van deze properties. Wegens tijdsgebrek is er niet verder gezocht naar een betere oplossingen en is de conditioner hier gestopt. De mogelijkheid om een conditie te maken is er, maar niet de vertaling naar een boolean.
39
Hoofdstuk 12
Kleine Uitbreidingen 12.1
Zoekbalk
Figuur 12.1: Het zoekpaneel Er was gevraagd het mogelijk te maken om te sorteren en zoeken in de toolbox met de controls. Hiervoor is er een extra paneel toegevoegd dat kan worden verstopt door op de verrekijker te klikken in de taakbalk. Er zijn 3 manieren waarop deze toolbox ordelijker kan worden gemaakt. De eerste is groeperen van de controls en de tweede is sorteren van de controls. private void c b S o r t B y S e l e c t i o n C h a n g e C o m m i t t e d ( object s e n d e r , EventArgs e ) { ComboBox cb = ( ComboBox ) s e n d e r ; i f ( cb . S e l e c t e d I n d e x == 0 ) //Name { l v C o n t r o l s . L i s t V i e w I t e m S o r t e r = new ListViewNameComparer ( ) ; } e l s e i f ( cb . S e l e c t e d I n d e x == 1 ) // Type
40
{ l v C o n t r o l s . L i s t V i e w I t e m S o r t e r = new ListViewTypeComparer ( ) ; } } public c l a s s ListViewNameComparer : IComparer { public ListViewNameComparer ( ) { } public i n t Compare ( object x , object y ) { L i s t V i e w I t e m xitem = ( L i s t V i e w I t e m ) x ; L i s t V i e w I t e m yitem = ( L i s t V i e w I t e m ) y ; return S t r i n g . Compare ( xitem . Text , yitem . Text ) ; } }
De derde manier is zoeken. Dit wordt gedaan via reguliere expressies. foreach ( L i s t V i e w I t e m l v i in a l l I t e m s ) { i f ( Regex . IsMatch ( l v i . Text , ” . ∗ ” + t x t S e a r c h . Text + ” . ∗ ” , RegexOptions . I g n o r e C a s e ) ) f o u n d I t e m s . Add( l v i ) ; }
12.2
Titel en Tooltip
Figuur 12.2: Titel & Tooltip Deze verandering was moeilijker dan oorspronkelijk gedacht. Dit komt door het Translationobject dat dit venster heeft. Een Translationobject is een object dat voor ´e´en zin, meerdere vertalingen bijhoudt. Het is niet toegelaten dat ´e´en component, in dit geval een tabblad, twee Translationobjecten heeft. Het is namelijk niet mogelijk om dit in de databank op te slaan zonder een veld toe te voegen. Dit is de reden waarom het samengevoegd was. De eerste poging om dit probleem op te lossen was een veld toe te voegen in de databank. Dit liep relatief vlot tot de definities van alle andere Translationobjecten moesten worden aangepast. Dit waren er veel en daarom is de beslissing gevallen om het veld terug te verwijderen. De tweede en laatste poging was het origineel samengevoegd bericht te behouden. Het bericht zou vervolgens voor het uitschrijven en bewaren, gesplitst en
41
terug samengevoegd worden. Op deze manier is er nog steeds maar ´e´en Translationobject per component en hoeven er geen databankveranderingen plaats te vinden.
12.3
Zoom
Er zijn veel mensen die via windows hun scherm laten uitzoomen omdat ze de tekst te klein vinden. Dit zorgde ervoor dat alle afstanden in de zelfgemaakte layouts veranderden. Labels werden onder controls getekend, controls stonden veel te dicht bij elkaar,... Om dit te corrigeren moeten we eerst zien te achterhalen wat de zoom is. Dit kunnen we vinden door de dots per inch(dpi) op te vragen.[8] public double getZoom ( System . Drawing . G r a p h i c s g ) { f l o a t dpiX ; dpiX = g . DpiX ; i f ( 1 1 0 <= dpiX && dpiX <= 1 3 0 ) return 1 . 2 5 ; e l s e i f ( dpiX > 1 3 0 ) return 1 . 5 0 ; else return 1 . 0 ; }
Vervolgens moeten alle breedte-, hoogte- en locatievariabelen vermenigvuldigd worden met deze zoom. Bij het opslaan moet de zoom natuurlijk eerst weer verwijderd worden. Anders is het mogelijk de zoom op elkaar te stapelen en dit is niet gewenst. De methode getZoom() wordt geplaatst in DIASMan zodat alle klassen die deze functie nodig hebben deze kunnen gebruiken.
42
Deel IV
Andere
43
Hoofdstuk 13
Na uitbreidingen 13.1
Handleiding
Tijdens het werk aan de uitbreidingen is er ook een handleiding geschreven. Aangezien er vele nieuwe features in de UID zijn geplaatst, is dit een noodzakelijk werk. In deze handleiding staan alle grote en de meeste kleine veranderingen aangeduid en uitgelegd door middel van tekst en screenshots. Deze handleiding is te vinden in de bijlagen. Op het einde van het coderen is deze handleiding overlopen met een collega die niet is gespecialiseerd in programmeren.
13.2
Testen
Bij C&C worden alle testen uitgevoerd door het programma te gebruiken. Hierdoor zijn de meeste testen gebeurd door zelf de code te builden en de UID te gebruiken. Zoals hierboven vernoemd, is er uitleg gegeven aan een collega. Deze collega heeft voor en na de uitleg ook de UID gebruikt en heeft alle fouten en onhandigheden in een excelbestand ingevoerd. Zo kon het excelblad overlopen worden om de overige bugs eruit te halen.
44
Besluit In het algemeen ben ik zeer tevreden met het resultaat. Er zijn geen zware complicaties opgetreden. Hierdoor waren de kerntaken, beschreven in het uitgebreid voorstel, relatief vlug uitgewerkt. Dit heeft mij de gelegenheid gegeven om aan andere zaken te werken zoals het versiebeheer. Het is bij deze uitbreiding dat ik het meest heb geleerd. Er moest namelijk veel met PROGRESS gewerkt worden. Aangezien vele zaken zoals procedures en indexen enkel theorie waren in de cursus, heb ik deze nu in praktijk kunnen toepassen. De conditioner was een extra feature die ook gestart werd, maar niet kon afgewerkt worden door de complicaties beschreven op blz 38. Dit toont dat elk werk nog voor verbetering vatbaar is. De ideen werden aangereikt, en mits extra tijd voor (o.a.) grootschalige testen, zal deze conditioner ook ingepast kunnen worden in het voorliggende geheel. Tot slot: er werd een werkend programma afgeleverd, dat veel meer mogelijkheden bezit dan zijn voorganger.
45
Lijst van figuren 2.1
De UID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
4.1 4.2
UML van het memento pattern . . . . . . . . . . . . . . . . . . . UML van het gewijzigde memento pattern . . . . . . . . . . . . .
10 11
6.1
Deel van de werkbalk om vormen toe te voegen . . . . . . . . . .
18
7.1 7.2 7.3
Een nieuwe layout maken . . . . . . . . . . . . . . . . . . . . . . Een layout openen . . . . . . . . . . . . . . . . . . . . . . . . . . Subtypes veranderen . . . . . . . . . . . . . . . . . . . . . . . . .
23 24 25
9.1 9.2
Relaties nieuwe databank . . . . . . . . . . . . . . . . . . . . . . Menu voor versiebeheer . . . . . . . . . . . . . . . . . . . . . . .
29 35
11.1 De originele conditionbuilder . . . . . . . . . . . . . . . . . . . .
39
12.1 Het zoekpaneel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.2 Titel & Tooltip . . . . . . . . . . . . . . . . . . . . . . . . . . . .
40 41
1
49
Schema van de oude databank . . . . . . . . . . . . . . . . . . . .
46
Bibliografie [1]
J Sadd. OpenEdge Development: Progress 4GL Handbook. Red. door Progress Software Corporation. isbn: ISBN 0-923562-04-4.
[2]
G Kassabgi. Special Edition : Using Progress. Red. door Que Publishing. isbn: 0-7897-0493-5.
[3]
LLC Data & Object Factory. Memento. 2014. url: http://www.dofactory. com/Patterns/PatternMemento.aspx.
[4]
Sean Yixiang Lu. Generic Memento Pattern for Undo-Redo in C#. 2007. url: http://www.codeproject.com/Articles/18025/Generic-MementoPattern-for-Undo-Redo-in-C.
[5]
bsargos. Filtering properties in a PropertyGrid. 2006. url: http://www. codeproject . com / Articles / 13342 / Filtering - properties - in - a PropertyGrid.
[6]
Ahmad Mageed. Sort with two criteria, string ascending, int ascending. 2010. url: http : / / stackoverflow . com / questions / 2376628 / sort with-two-criteria-string-ascending-int-ascending.
[7]
King King. How to know if RichTextBox vertical Scrollbar reached the max value? 2013. url: http://stackoverflow.com/questions/19343794/ how- to- know- if- richtextbox- vertical- scrollbar- reached- themax-value.
[8]
David Heffernan. How to get Windows Display setting? 2011. url: http: / / stackoverflow . com / questions / 5977445 / how - to - get - windows display-settings.
47
Deel V
Bijlagen
48
Oud databankschema
Figuur 1: Schema van de oude databank
49
Handleiding uitbreidingen UID Lente 2014
Inhoudstafel Inleiding ................................................................................................................................................... 1 NOODZAKELIJK INDIEN UPDATE JUIST GEÏNSTALLEERD ......................................................................... 1 1 Taakbalk................................................................................................................................................ 1 2Subtypes ................................................................................................................................................ 1 2.1 Nieuwe layout ............................................................................................................................... 1 2.2 Open layout ................................................................................................................................... 2 2.3 Veranderen at runtime .................................................................................................................. 3 3 De Toolbox............................................................................................................................................ 4 4 Tabbladen ............................................................................................................................................. 5 4.1 Vertical tabbladen ......................................................................................................................... 5 4.2 Uitzicht........................................................................................................................................... 5 4.3 Eigenschappen............................................................................................................................... 5 4.4 Tabvolgorde ................................................................................................................................... 6 4.5 Ouder veranderen ......................................................................................................................... 6 5 Versiebeheer ........................................................................................................................................ 6 5.1 Openen .......................................................................................................................................... 7 5.2 Verwijderen ................................................................................................................................... 7 5.3 Beheer ........................................................................................................................................... 8 6 Functionaliteit ...................................................................................................................................... 8 6.1 Undo/redo ..................................................................................................................................... 8 6.2 Multiselect veranderen ................................................................................................................. 8 6.3 Resize ............................................................................................................................................. 8 6.4 Tooltip............................................................................................................................................ 8 6.5 Delete ............................................................................................................................................ 9 6.6 Scrolling ......................................................................................................................................... 9 6.7 Tabindexering ................................................................................................................................ 9 6.8 Afstand label .................................................................................................................................. 9
Inleiding In deze handleiding worden de veranderingen van de UID uit de doeken gedaan. Het is aangeraden om de handleiding “Administrator Manual 2013” te lezen voor u dit leest, omdat hier enkel de nieuwe mogelijkheden besproken worden.
NOODZAKELIJK INDIEN UPDATE JUIST GEÏNSTALLEERD Voor je begint te werken met Dias, moeten er twee zaken gedaan worden. Het databank script laten runnen zodat alles correct is opgevuld Alle layoutsopnieuw laten saven. Om dit te doen ga je naar de UID en klik je in de taakbalk op
.
1 Taakbalk
1) Zie 5.2 2)Zie 3 3)Zie 2.3 4) Zie 6.7 5) Zie 6.1 6) Zie 6.6
2Subtypes 2.1 Nieuwe layout
1
Zoals u kan zien is de selectiebox vervangen door een selectielijst. Het is nu mogelijk om een layout meerdere subtypes te geven zodat u niet dezelfde layout meerdere keren hoeft te maken. Indien er geen subtype(s) geselecteerd is, zal “Standaard” als subtype gelden.
2.2 Open layout
Hier is een nieuwe kolom toegevoegd, namelijk Aantal. Deze kolom geeft aan hoeveel subtypes de layout beschrijft. Onder subtype worden de meerdere subtypes gescheiden door //.
2
2.3 Veranderen at runtime
Indien je het subtype nog wilt veranderen als je de layout hebt aangemaakt, kunt u op klikken in de taakbalk. Dit zal bovenstaand venster openen. Hier ziet u de naam, het type en de meerdere subtypes van de layout. Hier kunt u, net zoals u een nieuwe layout maakt, andere subtypes selecteren.
3
3 De Toolbox Het is mogelijk om te zoeken en sorteren in de toolbox. Het zoekveld is enkel zichtbaar wanneer gewenst. Om het zoekveld te tonen of te verstoppen klikt u op
in de taakbalk.
Groepeer zorgt ervoor dat de componenten gegroepeerd worden volgens de geselecteerde norm. Je hebt drie normen: Algemeen, Type en Alfabetisch. Algemeen zet de componenten apart die niet uniek zijn voor de layout en dus meerdere keren gebruikt kunnen worden. Type plaatst de controls in groepen volgens het soort component en alfabetisch zet de controls tesamen volgens hun eerste letter of cijfer. Sorteren kan op twee normen: Naam en Type. Beide zijn alfabetisch, maar het eerste is op naam van de component, de tweede is op het type van de component. Via zoek kan je zoeken naar een component volgens drie normen: Naam, Type en Code. U hoeft enkel te selecteren waarop u wilt zoeken en de zoekterm in het tekstveld eronder schrijven. Er zijn ook enkele extra componenten toegevoegd. Deze zijn allemaal Algemeen en zijn extra vormen om een mooiere layout te kunnen maken. De extra componenten zijn:
Lijn (Horizontaal) Lijn (Verticaal) Ovaal
Elk van deze componenten heeft een extra property lijndikte meegekregen, deze is te vinden onder"Control specific"in het propertyvenster
4
4 Tabbladen 4.1 Vertical tabbladen
Om een verticaal tabniveau toe te voegen klik je enkel op “tab niveau toevoegen (Verticaal)”. De andere opties hebben dezelfde functionaliteit als voorheen.
4.2 Uitzicht
De tabbladen plakken niet meer aan elkaar. Er is nu plaats voorzien waar u componenten kan plaatsen. Deze plaats kan verstopt worden onder eigenschappen(zie onder)
4.3 Eigenschappen
5
Er zijn enkele nieuwe eigenschappen toegevoegd. De eerste is“Verstop paneel”. Zoals hierboven vernoemd laat deze optie toe om het extra paneel, tussen 2 tabs, te verstoppen. Indien de tab geen kindtabs heeft is deze optie overbodig. Een tweede optie is “Lettertype & kleur”. Deze laat toe om het lettertype en kleur van de tabheader te kiezen. Een voorbeeld wordt ernaast weergegeven. De derde en laatste optie is “Kleur header”. Deze laat toe de achtergrondkleur van de tabheader te veranderen. De knop zelf zal de gekozen kleur krijgen als voorbeeld.
4.4 Tabvolgorde Vroeger kon je de tabvolgorde enkel veranderen via eigenschappen. Nu is het ook mogelijk deze te verslepen naar de goede plaats.
4.5 Ouder veranderen
Je kan ook de tab verplaatsen naar een andere tablijn zoals zijn ouder of kind. Dit doe je door op "TabOuder veranderen" te klikken en in volgend scherm zijn nieuwe ouder te kiezen. Kinderen zullen niet zichtbaar zijn, aangezien je hiernaar niet kan verplaatsen.
5 Versiebeheer Er is nu de mogelijkheid om verschillende versies te maken van je layouts. Zo kan je een layout aanpassen zonder dat andere mensen meteen de nieuwe layout te zien krijgen.
6
5.1 Openen
Zoals je hierboven kan merken, is het openen van een layout gewijzigd ten opzichte van de vroegere situatie. In het eerste venster kies je zoals vroeger het type layout en krijg je van de laatst aangemaakte versie zijn nummer, naam(omschrijving) en subtypes te zien. Indien er maar één versie bestaat zal deze meteen geopend worden. Indien er echter meerdere zijn, zal je in een nieuw venster deze versies te zien krijgen. Hierin zie je het versienummer, zijn aantal subtypes en de subtypes zelf, zijn omschrijving, of het een systeemlayout is en of de versie is ingecheckt.
5.2 Verwijderen Zoals in de toolbalk te zien, zijn er twee verwijdericonen. De vuilbak zal de layoutversie verwijderen, de map met vuilbak zal de hele layout met alle versies verwijderen.
7
5.3 Beheer
Onder instellingen vind je een nieuwe tab versiebeheer. Hierin kan je verschillende instellingen aanpassen. Ten eerste kan je de versie uit- of inchecken. Je kan ook een nieuwe versie aanmaken. Deze versie moet hoger zijn dan de vorige. De lijst met gebruikers zijn de gebruikers die de laatste versie te zien zullen krijgen en niet de laatst uitgecheckte versie.
6 Functionaliteit 6.1 Undo/redo Je kan bepaalde zaken ongedaan maken, zoals verwijderen,toevoegen en veranderen van componenten. Je kan undo oproepen door op
te klikken in de taakbalk of de shortcutCtrl-Z te
gebruiken. Redo kan gebruikt worden door op
te klikken of shortcutCtrl-Y te gebruiken.
6.2 Multiselect veranderen Het is mogelijk om multiselect te gebruiken om een property op meerdere componenten inéén keer te veranderen.
6.3 Resize
Componenten kan je resizen door te slepen. U hoeft dus niet meer via de properties de waardes handmatig in te geven. Hier kan ook gebruik van gemaakt worden als er meerdere componenten zijn geselecteerd.
6.4 Tooltip Er kan via het propertiesveld een tooltip worden toegevoegd aan de component. Bij sommige velden zal deze tooltip enkel zichtbaar zijn als je over de label blijft stilstaan.
8
6.5 Delete Je kan componenten verwijderen door de te verwijderen componenten te selecteren en op de deletetoets te drukken.
6.6 Scrolling Indien je een paneel kiest zonder tabbladen, dan zal het paneel zich automatisch vergroten indien je een control langs de zijkant verplaatst. Je kan de snelheid van het scrollen aanpassen in de toolbalk.
6.7 Tabindexering Zoals te zien in de toolbalk heeft de tabindexering nu 3 mogelijkheden. De eerste twee zijn alle controls en tabs automatisch indexeren volgens positie. Verticaal zal eerst naar beneden gaan en vervolgens de controls ernaast, Horizontaal doet het omgekeerde. Manueel is zoals het vroeger was. De keuze wordt bewaard en indien je savet zal de tabvolgerde worden aangepast indien het op horizontaal of verticaal staat. De tabindexering is heel gevoelig op de hoogte van de controls. Hier moet je dus goed op letten. Indien het lijkt dat de tabindexering niet correct is, kijk zeker na of de controls op dezelfde hoogte staan.
6.8 Afstand label In het propertgrid kan je de afstand tussen het label en de control aanpassen. Dit kan zowel horizontaal als verticaal. Dit is respectief LabelDistance X en LabelDistance Y.
9
Eigen analyse Database veld Version => String; Conversie van string naar double om versie te vergelijken is gemakkelijk. Conversie van decimal, double naar string => laat de 0 vallen bv 1.0 -> 1 Via script alles op 1.0 zetten in het begin. Veld in UIObject of UILayout? UILayout: zo houdt je in het openObject menu steeds maar één mogelijkheid. UILayout moet ook veld “Actief” krijgen: Dit dient om te weten welk van de layouts gebruikt wordt buiten het testen. Dit veld mag maar bij 1 layout van hetzelfde object actief zijn. In UIObject moet de mogelijkheid er zijn om meerdere layouts op te halen: - Lijst met UILayout-obj - ////////UILayout krijgt een veld UIObject_obj; (of objects) In C# In vwUIDesigner: Bij setting veld version toevoegen. Hier staat de versie vermeld en een knop om een nieuwe versie op te geven. Er moet een mogelijkheid zijn om terug te keren naar een vorige layout, maar de versie blijft hetzelfde. Zo kan je terug vertrekken van een oude layout, maar kan je de versies niet verknoeien.
Nieuw versie venster: Hier kan je een nieuwe versie opgeven. Deze moet groter zijn dan de laatste versie en mag enkel bestaan uit cijfers en punten. Ook worden alle versie opgesomd en kan je de actieve versie kiezen.
In frmUIObject (het open venster): Kolom versie bijzetten. Laatste of actieve versie laten zien. In UIObject: Indien je een uiobjectophaald via frmUIObject moet de laatste versie getoond worden. In de meeste andere gevallen moet de actieve getoond worden. Hier moeten dus 2 ophaalfuncties zijn string prep = string.Format("FOR EACH uilayoutWHERE uilayout.Active EQ yesAND (STRING({1}) MATCHES (“*”+uilayout.uilayout_obj+”*”)
{1} is de lijst met uilayout_obj. In de reguliere expressie moet uilayut.uilayout_obj een string zijn geen decimal
DEFINE VARIABLE imaxAS INTEGER no-undo. FOR EACHuiLayout (WHEREUIobject_obj ={1}) BREAK BY uiLayout.uiLayout_obj: ACCUMULATEversion(MAXIMUM BY uiLayout.uiLayout_obj). IF LAST-OF(uiLayout.uiLayout_obj) THEN DO: ASSIGNimax=(accum maximum version). END. END. FOR EACH uilayout where version=imax
OF DEFINE VARIABLE imaxAS INTEGER no-undo. FOR EACH uiLayout(WHERE(STRING({1}) MATCHES (“*”+uilayout.uilayout_obj+”*”)) BREAK BY uiLayout_obj: IF FIRST-OF(uiLayout_obj) THEN ASSIGNimax=-999999. ASSIGNimax=MAX(imax,width). /*IF last-of(uiLayout_obj) THEN displayimax.*/ END. FOR EACH uilayoutWHERE version=imax
Break by moet gebeuren op iets me een index. In de where kan je weer de lijst meegeven zodat je de correcte layouts hebt. PROBLEMEN Hoe kan je versies testen buiten de vwUIDesigner? - Soort algemene variabele in DIAS? Indien true zie je de laatste versies in plaats van de actieve In DIASMan kan deze variabele gezet worden. UIObjectenlocken als er in vwDesigner aan gewerkt wordt? - Anders kunnen er 2 overlappende versies worden gemaakt.
Na Bespreking Een nieuwe tabel aan te maken UILayoutVersion - UILayout - Major - Minor - Diasuser_obj (gebruiker die de versie gecreëerd heeft) - CheckOutMode (0 = ingechekt, 1 = uitgechekt)(Actief veld) - TestUsers (chr(3) seperated veld met obj’s van gebruikers die de layout kunnen zien nog voor die is ingecheckt voor testdoeleinden) In de tabel UILayoutVersion wordt dus verwezen naar UILayout In de tabel UIObject wordt verwezen naar UILayoutVersionipvUILayout In de tabel UITabFolder wordt verwezen naar UILayoutVersionipvUILayout In de tabel UIComponent wordt nu verwezen naar UILayout. Deze zal moeten verwijzen naar UILayoutVersion
Bij het laden van een layout in de UIDesigner zie je eerst de lijst van de UILayouts. Als je dan een bepaalde layout kiest waarvoor meerdere versies bestaan, dan krijg je een lijstje met de diverse versies zodat je de gewenste versie kan inladen. Testen van de aanpassingen : hiervoor zullen we een chr(3) seperated veld voorzien om testuser obj’s bij te houden. Als deze gebruikers dus inloggen, dan zien ze niet de laatste ingecheckte versie maar wel de laatst gecreëerde versie. Ook de export en import van de packages zal aangepast moeten worden om de diverse versies mee te exporteren.