GNavigia selbst pro­gram­mie­ren

Er­wei­te­run­gen mit­tels C# hin­zu­fü­gen - Schritt für Schritt


Auf die­ser Sei­te wer­den wir die Fra­ge be­tra­chen, was Sie ma­chen kön­nen, wenn GNavigia trotz sei­ner zahl­rei­chen Funk­tio­nen par­tout nicht an­bie­tet, was Sie sich wün­schen. Neh­men wir ein­mal an, dass Sie ei­nen exo­ti­schen GPS-Emp­fän­ger be­sit­zen, der Din­ge tut, die ich nicht vor­her­sa­gen konn­te und die ge­wiss nicht zum Stan­dard zäh­len. Dann kön­nen Sie hier ein­grei­fen. Ein Bei­spiel:


Abb. 1: Weg­punk­te des Gar­min etrex Vis­ta HCx

Der GPS Emp­fän­ger Gar­min etrex Vis­ta HCx spei­chert Da­tum und Uhr­zeit ei­nes Weg­punkts in den At­tri­bu­ten Cmt und De­sc der all­ge­mein an­er­kann­ten Weg­punkt­be­schrei­bung. Das Da­ten­for­mat, das sich als Stan­dard eta­bliert hat, sieht für Weg­punk­te kein Zei­tat­tri­but vor. GNavigia kann so ein At­tri­but ver­wal­ten, al­ler­dings ist das am An­fang na­tur­ge­mäß leer. Sie wer­den im Lau­fe die­ser Do­ku­men­ta­ti­on ler­nen, wie Sie das Pro­blem mit­hil­fe ei­nes Cli­ents lö­sen kön­nen. Da­zu wer­den wir die Auf­nah­me­num­mer, die un­ter Na­me ein­ge­tra­gen ist, nach Cmt über­tra­gen und das At­tri­but Da­teAn­dTi­me fül­len. De­sc wer­den wir lö­schen.

Voraus­set­zun­gen

Um GNavigia selbst zu pro­gram­mie­ren be­nö­ti­gen Sie
  • Ein Ver­zeich­nis, in dem das Pro­gramm GNavigia.exe und sei­ne DLLs ent­hal­ten sind. Für die Do­ku­men­ta­ti­on hier neh­me ich C:\Pro­gram­me\GNa­vi­gia\GNa­vi­gia.exe an.
  • Ei­ne Ent­wick­lungs­um­ge­bung (C#, VB oder C++) für Mi­cro­soft .NET 2.0 (3.5 tut es auch), be­vor­zugt Mi­cro­soft Vi­su­al Stu­dio 2005 oder 2008. Als Pri­vat­mann be­nut­zen Sie zweck­mä­ßig die kos­ten­lo­se Ex­press-Edi­ti­on. Hier­auf be­zieht sich die Do­ku­men­ta­ti­on.
  • Kennt­nis­se über das In­ne­re von GNavigia, die Sie sich aus den Me­ta­da­ten an­eig­nen kön­nen. Ei­ne Do­ku­men­ti­on ist in Vor­be­rei­tung, wird aber den Rest die­ses Jah­res in An­spruch neh­men.
  • Grund­le­gen­de Kennt­nis­se der Pro­gram­mie­rung. Die fol­gen­den Aus­füh­run­gen rich­ten sich de­fi­ni­tiv nicht an An­fän­ger der Soft­wa­re­ent­wick­lung!
Ich hat­te zu­nächst vor, das GpsDa­taE­di­tor-Ob­jekt nicht frei­zu­ge­ben, aber wenn man das im fol­gen­den be­schrie­be­ne In­ter­face auf GpsDa­taE­di­tor cas­tet, hat man das Ob­jekt «in der Hand». Da­mit kön­nen Sie pro­gram­mie­ren, als lä­ge GNavigia im Quell­code vor. Auch den kön­nen Sie auf An­fra­ge be­kom­men, aber er ist nicht nö­tig, es sei denn, Sie wol­len mich tat­kräf­tig un­ter­stüt­zen! Neh­men wir ein­mal an, dass Sie nur ihr ei­ge­nes Süpp­chen ko­chen wol­len, dann wür­den Sie je­de Quell­co­de­än­de­rung nach­voll­zie­hen müs­sen. Die In­ter­face-Metho­den, die Ih­nen GNavigia an­bie­tet, wer­den sich al­ler­dings nur noch bei gro­ben Feh­lern än­dern. Dann kön­nen Sie ei­ne Pro­gram­mer­wei­te­rung schrei­ben, die auch in spä­te­ren Ver­sio­nen noch lau­fen wird. Set­zen Sie auf je­den Fall für den Ver­weis Spe­zi­fi­sche Ver­sion auf fal­se.

Das Cli­ent-Pro­jekt

Le­gen Sie in der Ent­wick­lungs­um­ge­bung ein neu­es Pro­jekt an und zwar ei­ne Klas­sen­bi­blio­thek; hier ha­be ich den spre­chen­den Na­men GNa­vi­giaGpsEle­ment­sC­li­ent ver­ge­ben. Neh­men Sie ei­nen an­de­ren, weil der sonst mit dem Na­men des Cli­ents bei der In­stal­la­ti­on von GNavigia kol­li­diert! Es ist gut, wenn der Na­me ein­deu­tig ist, wirk­lich ein­deu­tig sein muss am En­de aber der Na­me, mit dem sich der Cli­ent ge­gen­über GNavigia iden­ti­fi­ziert. Zu­nächst ge­nügt es, dem neu­en Pro­jekt die in Ab­bil­dung 2 an­ge­zeig­ten Ver­wei­se hin­zu­zu­fü­gen. Spä­ter brau­chen Sie ei­ni­ge mehr.
Ver­ge­ben Sie auch ei­nen star­ken Na­men, da­zu öf­fe­nen Sie die Ei­gen­schaf­ten des neu­en Pro­jekts, ge­hen nach Signie­rung und wäh­len in dem un­te­ren Feld als Schlüs­sel­da­tei <Neu...> aus. Im Dia­log ge­ben Sie ei­nen Da­tein­amen (oh­ne Pfad!) an, hier wie­der GNa­vi­giaGpsEle­ment­sC­li­ent. Der Na­me wird dem Pro­jekt hin­zu­ge­fügt; ich ver­schie­be den Na­men bei of­fe­nen Fens­tern nach Pro­per­ties, wo­bei auch der Schlüs­sel­na­me links un­ten an­ge­passt wird.
Wenn Sie die Ex­press-Edi­ti­on ein­set­zen, kön­nen Sie un­ter dem Rei­ter De­bug­gen kei­ne Appli­ka­ti­on an­ge­ben, die ge­st­ar­tet wer­den soll, wenn Sie Aus­füh­ren! sa­gen. Da­mit Sie die Appli­ka­ti­on GNavigia den­noch so­fort star­ten kön­nen, kön­nen Sie un­ter Buil­der­g­eig­nis­se/Post­buil­der­eig­nis die Appli­ka­ti­on an­ge­ben: C:\Pro­gram­me\GNa­vi­gia\GNa­vi­gia.exe. Spei­chern Sie die Ein­stel­lun­gen in­dem Sie die Ent­wick­lungs­um­ge­bung ver­las­sen. Wa­rum er­zäh­le ich das, ob­gleich Sie auf die­se Art den De­bug­ger trotz­dem nicht nut­zen kön­nen? Nun, es ent­steht ne­ben der Pro­jekt­da­tei ei­ne, die auf user en­det. Edi­tie­ren Sie die­se mit ei­nem Edi­tor, der Uni­code-Zei­chen ver­ar­bei­ten kann, und fü­gen Sie die fol­gen­den Zei­len ein:
<Star­tAc­ti­on>Pro­gram</Star­tAc­ti­on>
<Star­tPro­gram>C:\Pro­gram­me\GNa­vi­gia\GNa­vi­gia.exe</Star­tPro­gram>
Ent­fer­nen Sie nach dem Neu­start der Ent­wick­lungs­um­ge­bung zu­nächst das Post­buil­der­eig­nis und fül­len Sie die Fel­der Ar­beits­ver­zeich­nis und Be­fehls­zei­len­ar­gu­men­te im Rei­ter De­bug­gen aus. Die Pa­ra­me­ter wer­den auf das zu star­ten­de Pro­gramm be­zo­gen, nicht auf die DLL. Wer die Ex­press Edi­ti­on von C++ be­nutzt, kann die Ein­trä­ge üb­ri­gens di­rekt im Dia­log vor­neh­men.

Abb. 2: C# Ent­wick­lungs­um­ge­bung mit den wich­tigs­ten Ein­stel­lun­gen

Die Kon­fi­gu­ra­ti­on

Ich ha­be mir zum Ziel ge­setzt, die Ent­wick­lung ei­ner Er­wei­te­rung oh­ne Ad­mi­nis­tra­tor­rech­te zu er­mög­li­chen. Das er­for­dert, dass die DLL nicht re­gis­triert wer­den muss, wie das bei den Ser­vern für die Hin­ter­grund­bil­der not­wen­dig ist. Um sie GNavigia be­kannt zu ma­chen, be­die­nen wir uns der Kon­fi­gu­ra­ti­ons­da­tei, die je­der .NET Kom­po­nen­te bei­ge­stellt wer­den kann. Die­se Da­tei liegt im sel­ben Ver­zeich­nis wie GNavigia.exe selbst. Sie ist ei­ne XML-Datei na­mens GNavigia.exe.con­fig. Die­se Da­tei kön­nen Sie in je­dem her­kömm­li­chen XML-Edi­tor be­ar­bei­ten, brau­chen für die­sen Vor­gang ggf. aber ein­ma­lig Ad­mi­nis­tra­tor­rech­te. Ma­chen Sie ei­ne Si­che­rungs­ko­pie ih­rer Än­de­run­gen, da die­se Da­tei bei der nächs­ten In­stal­la­ti­on von GNavigia über­schrie­ben wer­den wird!

Abb. 3: Die Da­tei GNavigia.exe.con­fig

Im ers­ten Schritt gibt es für Sie zwei in­te­res­san­te Ein­trä­ge, lo­g4­net und GNavigia.GpsEle­mentHand­ler. Letz­te­rer lädt Ih­re DLL! Be­fas­sen Sie sich aber auch mit lo­g4­net. Der Grund: Wenn Sie die Pro­to­kol­l­aus­ga­be Ih­rer Soft­wa­re über die­sen Weg aus­ge­ben, ha­ben Sie ge­nau ei­ne Da­tei, die Sie be­ob­ach­ten müs­sen, näm­lich die, die in der lo­g4­net sec­ti­on un­ter va­lue des Pa­ra­me­ters Fi­le an­ge­ge­ben ist. Zu­dem ist die Aus­ga­be syn­chron zu der von GNavigia, was die Be­ur­tei­lung der Ab­läu­fe ver­ein­facht und er­for­dert zu­gleich nur sehr we­nig Over­head. Sie wer­den lo­g4­net im Quell­code wie­der­be­geg­nen. An die­ser Stel­le soll­ten Sie zu­nächst nur den Da­tein­amen än­dern, falls Ih­nen die Vor­ga­be nicht zu­sagt.
In die Kon­fi­gu­ra­ti­ons­da­tei wird un­ter ap­pSet­tings der Schlüs­sel GNavigia.GpsEle­mentHand­ler ein­ge­tra­gen mit dem Wert: Ih­re Cli­ent-DLL, voll­stän­di­ger Pfad­na­me. Aus Dar­stel­lungs­grün­den ha­be ich den Pfad stark ab­ge­kürzt. Wich­tig ist, dass die keys al­le ein­deu­tig sein müs­sen! Da­her müs­sen Sie für ei­ne zwei­te DLL de­ren key um be­lie­bi­ge (gül­ti­ge) Zei­chen er­wei­tern. GNavigia lädt hem­mungs­los al­les, was mit GNavigia.GpsEle­mentHand­ler be­ginnt.
Zer­stö­ren Sie bei Än­de­run­gen nicht die Struk­tur der XML-Datei, da sonst auch GNavigia selbst nicht mehr rich­tig funk­tio­niert.
Um lo­g4­net nut­zen zu kön­nen, müs­sen Sie den Ver­weis auf die DLL hin­zu­fü­gen. Auch die­se fin­den Sie im In­stal­la­ti­ons­ver­zeich­nis von GNavigia.

Der Quell­code - Teil I

Wenn Sie beim An­le­gen des Pro­jekts kei­ne Feh­ler ge­macht ha­ben, soll­ten Sie ei­ne Quell­co­de­da­tei an­tref­fen, die ru­di­men­tär aus­ge­füllt ist. Sie kön­nen die Da­tei GNa­vi­giaGpsEle­ment­sC­li­ent.cs als Be­stand­teil die­ser Do­ku­men­ta­ti­on la­den, al­ler­dings kann der In­halt von der Be­schrei­bung hier ab­wei­chen, da ich mir vor­be­hal­te, die Da­tei für wei­te­re The­men zu mo­di­fi­zie­ren. Die Lau­fend­hal­tung der zahl­rei­chen Bild­schirm­fo­tos ist mit ver­tret­ba­rem Auf­wand oh­ne­hin nicht mög­lich.
Die Aus­gangs­da­tei muss in man­nig­fa­cher Hin­sicht er­gänzt wer­den, so zu­nächst um die Re­fe­ren­zen auf die drei KorKarNet (ich brauch­te ei­nen ein­deu­ti­gen Na­men!) Kom­po­nen­ten. Dann fol­gen die Re­fe­ren­zen, die Sys­tem.Col­lec­ti­ons, die wir brau­chen, weil die An­fän­ge der Ent­wick­lung nach .NET 1.1 zu­rück rei­chen, so­wie Sys­tem.Re­flec­ti­on, die wir für die auf Re­fle­xi­on ba­sie­ren­de Aus­ga­be der Metho­den­na­men be­nö­ti­gen, so­wie Sys­tem.Win­dows.Forms für das Ein­hän­gen in das Appli­ka­ti­ons­me­nü so­wie den Auf­ruf von Dia­log­fel­dern und die Aus­ga­be von Feh­ler­mel­dun­gen.
Die Klas­se selbst muss pu­blic an­ge­legt wer­den, was zu über­prü­fen wä­re. Als Na­me­space neh­men Sie ent­we­der den Pro­jekt­na­men oder ei­nen, den Sie auch für an­de­re Ent­wick­lun­gen be­nutzt ha­ben. Für die Funk­ti­on un­er­läss­lich ist die Ablei­tung der Klas­se von IGpsEle­mentC­li­ent. Schla­gen Sie nach dem Ein­trag das Kon­text­me­nü über dem Wort auf und wäh­len Sie aus der Lis­te Schnitt­stel­le ex­pli­zit im­ple­men­tie­ren aus. Da­mit wer­den die Metho­den, die mit IGpsEle­mentC­li­ent mem­bers, ex­pli­cit im­ple­men­ta­ti­on ge­klam­mert sind, im­ple­men­tiert wer­den. Da­zu spä­ter mehr. Ih­re Klas­se soll­te aus fol­gen­den Ab­schnit­ten (#re­gi­on/#end­re­gi­on) be­ste­hen:
  • Log­ger
  • Va­ria­blen und Kon­truk­to­ren
  • Metho­den der IGpsEle­mentC­li­ent-Schnitt­stel­le
  • Pri­va­te Metho­den

Wir wer­den die­se Ab­schnit­te nach und nach auf­klap­pen und nach­se­hen, wie sie im Ein­zel­nen im­ple­men­tiert sind.


Abb. 3: Die Da­tei

Um in­ner­halb der Klas­se mit der Va­ria­blen log auf die Aus­ga­be zu­grei­fen zu kön­nen, wird ein log­ger an­ge­legt mit voll­stän­di­gem Na­men, d.h. in­kl. Na­me­space. Da­durch ent­fal­len using-Direk­ti­ven. Die De­kla­ra­ti­on be­sagt meist pro­tec­ted, so­dass Sie nicht mehr dar­über nach­den­ken müs­sen, wenn Sie ei­ne Klas­se von die­ser Klas­se ab­lei­ten wol­len, aber das steht ih­nen frei. lo­g4­net kennt Metho­den zur Aus­ga­be von Zei­chen­ket­ten (log.De­bug, log.In­fo) und zur For­ma­tie­rung der­sel­ben, die dann z. B. log.In­foFor­mat hei­ßen und die Pa­ra­me­ter an String.For­mat wei­ter­rei­chen.

Abb. 4: Der Block Log­ger (lo­g4­net)

Wenn Sie nie mit Schnitt­stel­len ge­ar­bei­tet ha­ben, dann wird es Zeit, das jetzt zu tun. Schla­gen Sie die Hil­fe auf und be­fra­gen Sie die .NET Do­ku­men­ta­ti­on. Fakt ist, wenn Sie be­haup­ten, dass Sie ei­ne Klas­se von ei­ner Schnitt­stel­le ab­ge­lei­tet ha­ben, dann wacht der Com­pi­ler dar­über, dass Sie die dort ge­for­der­ten Metho­den auch im­ple­men­tie­ren.
Sie wis­sen, dass C# kei­ne Mehr­fach­ver­er­bung kennt. Schnitt­stel­len wer­den zwar wie Ba­sis­klas­sen no­tiert, sind aber kei­ne. Da­her kön­nen Sie be­lie­big vie­le Schnitt­stel­len an­ge­ben. Und ich ver­si­che­re Ih­nen, dass Sie das zur Nut­zung zu­künf­ti­ger Funk­tio­na­li­tät auch müs­sen! Das wich­tigs­te an Schnitt­stel­len ist, dass sie sich nicht mehr ver­än­dern, wenn sie ein­mal ver­öf­fent­licht sind. Da­ran hal­te ich mich jetzt. Schnitt­stel­len, die ge­än­dert wer­den müss­ten, er­hal­ten Pen­dants, es gibt dann al­so neue Schnitt­stel­len.
Es gibt zwei Ar­ten, Schnitt­stel­len zu er­zeu­gen, im­pli­zit und ex­pli­zit. Ich set­ze die Theo­rie als be­kannt vor­aus. In die­sem Bei­spiel wer­den wir Schnitt­stel­len­me­tho­den grund­sätz­lich ex­pli­zit de­kla­rie­ren, al­so pri­va­te (aber oh­ne Schlüs­sel­wort) und mit dem Prä­fix des Schnitt­stel­len­na­mens. Im vor­lie­gen­den Fall ha­ben wir es mit ge­nau ei­ner Schnitt­stel­le zu tun, IGpsEle­mentC­li­ent, und de­ren Metho­den. Da­mit der Bild­schirm nicht über­läuft, schau­en wir sie uns Metho­de für Metho­de an. Die Va­ria­blen, die im Spiel sind, wer­den wir ana­ly­sie­ren, so­bald de­ren Typ wich­tig wird. Zu­nächst sind das meist Zei­chen­ket­ten.


Abb. 5: Kon­struk­tor

Der Quasi-Kon­struk­tor tut nichts, au­ßer ei­ner Aus­ga­be ins Log­fi­le und der Si­che­rung der Ser­ver­re­fe­renz, die über ei­ne Schnitt­stel­le rea­li­si­sert wird, die der Ser­ver zur Ver­fü­gung stellt. Hier se­hen wir auch den Vor­teil der Re­fle­xi­on: Wir kön­nen die ers­te Zei­le in je­de Metho­de ko­pie­ren und be­kom­men im­mer den rich­ti­gen Na­men aus­ge­ge­ben. GNavigia nutzt Re­fle­xi­on üb­ri­gens auch für ei­ne ex­trem ein­fa­che Art der Im­ple­men­tie­rung von Un­do/Re­do.
Des wei­te­ren er­kennt man, dass ich Klas­sen­va­ria­ble im­mer mit "m_" an­fan­gen las­se. Für den Kon­struk­tor ist re­turn true Pf­licht, sonst wird die Ver­bin­dung nicht wei­ter ver­folgt!


Abb. 6: Das Cli­ent-Menü

Viel­leicht soll­te ich kurz er­klä­ren, wie GNavigia die Ver­bin­dung zum Cli­ent her­stellt. Der Na­me der DLL wird aus der Kon­fi­gu­ra­ti­on ge­le­sen, die DLL dy­na­misch ge­la­den und un­ter­sucht, wel­che Klas­se das IGpsEle­mentC­li­ent In­ter­face im­ple­men­tiert. Dann wird ei­ne In­stanz der Klas­se er­zeugt und die Metho­de Ini­tia­li­ze am In­ter­face auf­ge­ru­fen. Da­bei wird ei­ne Re­fe­renz auf das IGpsEle­mentSer­ver In­ter­face des Ser­vers an den Cli­ent über­ge­ben. Die­ser merkt sich die Re­fe­renz und ruft spä­ter sei­ner­seits Metho­den an die­sem In­ter­face auf, u. a. wer­den wir die Lis­te al­ler Weg­punk­te be­nö­ti­gen, die dem Cli­ent auf die­se Art und Wei­se be­reit­ge­stellt wird.

Nach er­folg­rei­cher Ini­tia­li­sie­rung fragt der Ser­ver den Cli­ent zu­nächst ein­mal, ob die­ser ein Me­nü be­reit­stel­len möch­te, was die­ser im­mer mit ei­ner gül­ti­gen Menü­re­fe­renz be­ant­wor­ten soll­te. Das Me­nü des Cli­ents muss zu­gleich den be­nö­tig­ten EventHand­ler be­reit­stel­len. Was der en de­tail tut, se­hen wir uns im Teil II an, wo wir auf die Än­de­rung von At­tri­bu­ten an GPS-Ele­men­ten nä­her ein­ge­hen und das IGpsEle­mentSer­ver In­ter­face be­trach­ten wer­den. Zu­nächst de­fi­nie­ren wir ei­ne OnMe­nuItemClicked Metho­de, die wir al­len Me­nuItem Ein­trä­gen mit­ge­ben. In der Metho­de un­ter­schei­den wir die Menü­punk­te an­hand des Na­mens, da­her müs­sen wir hier den Menü­punk­ten ein­deu­ti­ge Na­men zu­ord­nen. Das Am­per­sand kenn­zeich­net den Mne­mo­nic, al­so den un­ter­stri­che­nen Buch­sta­ben im Me­nü. Zu­letzt er­zeu­gen wir das Haupt­me­nü, das die Be­zeich­nung Cli­ent be­kommt, und wei­sen die­sem die Lis­te der Un­ter­menü­punk­te zu. Zu­gleich er­zeu­gen wir ei­ne OnMe­nuPo­pup Metho­de, die uns spä­ter in die La­ge ver­set­zen wird, vor dem Auf­klap­pen des Menüs die­je­ni­gen Menü­punk­te aus­zu­grau­en, die oh­ne­hin nicht er­reich­bar sind. Wenn Sie nur ei­nen ein­zi­gen Menü­punkt ha­ben, brau­chen Sie auch kei­ne Menühier­ar­chie. Ge­ben Sie ei­nen ein­zel­nen Menü­punkt zu­rück.


Abb. 7: OnMe­nuPo­pup-Event

Ei­ne OnMe­nuPo­pup Metho­de ist in für den Fall nutz­los, da Sie ein ein­zel­nes Me­nuItem zu­rück­ge­ben. Das Me­nü wird von GNavigia näm­lich wie folgt be­rück­sich­tigt: Be­sitzt das Me­nü Un­ter­punk­te, so wird es nach dem Me­nü Ex­tras im Haupt­me­nü der Appli­ka­ti­on an­ge­zeigt, es sei denn, Sie hät­ten den zwei­ten Pa­ra­me­ter, die Re­fe­renz fAd­dToMainMe­nu auf fal­se ge­setzt. In die­sem Fall und falls es kei­ne Un­ter­punk­te gibt, wird das Me­nü am En­de des Menüs Ex­tras ein­ge­reiht. Das ers­te der ver­füg­ba­ren Menüs wird, falls meh­re­re Cli­ents in der Kon­fi­gu­ra­ti­on ein­ge­tra­gen sind, mit ei­ner Trenn­li­nie von den vor­aus­ge­hen­den Menü­punk­ten ab­ge­ho­ben. Die Un­ter­brin­gung im Haupt­me­nü ist in Ab­bil­dung 6 rechts un­ten ein­ge­ar­bei­tet.


Abb. 8: Das OnPaint-Event

Da­mit der Cli­ent in der La­ge ist, Ob­jek­te selbst zu zeich­nen, be­nö­tigt er so­wohl An­ga­ben zur Zei­chen­flä­che als auch zum Zeit­punkt der Ak­tua­li­sie­rung der Dar­stel­lung. Zeich­nen Sie nie­mals un­auf­ge­for­dert! War­ten Sie auf das Si­gnal!
Das OnPaint-Event macht es Ih­nen leicht. Es reicht ein GpsDa­taE­di­tor.GpsGra­phics Ob­jekt in die Metho­de hin­ein, das nicht nur die er­for­der­li­chen An­ga­ben ent­hält, son­dern auch Metho­den, die es er­lau­ben, ent­spre­chend den ak­tu­el­len Ein­stel­lun­gen Tex­te aus­zu­ge­ben. Ins­be­son­de­re Metho­den zur Frei­stel­lung von Text sind hier er­wäh­nens­wert.
Das Er­eig­nis lie­fert auch ei­nen Hin­weis dar­auf, ob man sich am An­fang oder am En­de der Zei­chen­fol­ge be­fin­det. Ist fOnBe­ginPaint fal­se, zeich­net man on top, sonst vor al­len an­de­ren Rou­ti­nen. Ei­ne ein­ge­hen­de­re Be­schrei­bung folgt, wenn Zeich­nen The­ma der Er­läu­te­run­gen ist. Für un­ser Bei­spiel be­nö­ti­gen wir kei­ne ein­ge­nen Zei­chen­rou­ti­nen.


Abb. 9: Die Events OnRea­dDa­ta/OnWri­teDa­ta

Da­mit der Cli­ent ei­ge­ne Da­ten ver­wal­ten kann, stellt GNavigia ei­ne Mög­lich­keit be­reit, die­se Da­ten zu­sam­men mit de­nen der Appli­ka­ti­on in die GTD-Datei­en zu schrei­ben. Um zu ver­hin­dern, dass ein Feh­ler des Cli­ents zum Ver­lust der Ori­gi­nal­da­tei führt, wur­de das Spei­chern über­ar­bei­tet. Erst wenn for­mal kei­ne Feh­ler fest­ge­stellt wur­den, wird die al­te Da­tei durch die neue er­setzt. Wenn der Cli­ent ei­ne nicht mehr kon­sis­ten­te XML-Struk­tur hin­ter­lässt, kann das hin­ge­gen nicht fest­ge­stellt wer­den. Prü­fen Sie die Da­tei nach je­dem Spei­chern. La­den Sie sie in die Ent­wick­lungs­um­ge­bung und las­sen Sie ggf. spe­zi­el­le XML-Edi­to­ren Feh­ler im in­ne­ren XML su­chen.
Der in­ne­re XML-Code, der vom Cli­ent beim Auf­ruf von OnWri­teDa­ta an GNavigia zu­rück­ge­ge­ben wird, be­steht aus ei­ner ein­zi­gen Zei­chen­ket­te, die in die GTD-Datei ein­ge­bet­tet wird. Je­der Cli­ent kann ge­nau ei­ne Zei­chen­ket­te zum Spei­chern zu­rück­ge­ben. Das in­ne­re XML kann be­lie­big kom­plex sein. Das Er­geb­nis des o. a. Co­des ist im fol­gen­den Bild fest­ge­hal­ten, die Zei­chen­ket­te im At­tri­but na­me ist der, der in der Va­ria­blen cli­entNa­me zu­rück­ge­ge­ben wird.


Abb. 10: Das Er­geb­nis von OnWri­teDa­ta

Hin­weis: Zur­zeit gibt es kei­ne An­fra­ge an den Cli­ent, ob sei­ne Da­ten ge­spei­chert wer­den müs­sen. Die­ses Pro­blem soll­te vor lan­ger zeit «in ei­ner der nächs­ten Ver­sio­nen» be­ho­ben wer­den. Das ist lei­der im­mer noch nicht der Fall.
Zu­letzt wer­den wir im Teil I ei­nen Blick auf den Kon­struk­tor und die Klas­sen­va­ria­blen wer­fen. Die Re­fe­renz zum Ser­ver IGpsEle­mentSer­ver wird zum Zu­griff auf die Da­ten be­nö­tigt. Das In­ter­face stellt fol­gen­de Metho­den zur Ver­fü­gung:

  • TreeView GpsOb­jec­tTree() - Wur­zel­ele­ment des Ob­jekt­baums. Ein The­ma für Fort­ge­schrit­te­ne.
  • List<GpsTrack­point> GpsTrack­points(GpsTrack gpsTrack) - Lis­te al­ler Track­points ei­nes Tracks.
  • List<GpsTrack> GpsTracks() - Lis­te al­ler ver­füg­ba­ren Tracks.
  • List<GpsWay­po­int> GpsWay­po­ints() - Lis­te al­ler Weg­punk­te.
  • IGpsDa­taE­di­tor IGpsDa­taE­di­tor() - In­ter­face, das aus­ge­wähl­te Metho­den des GpsDa­taE­di­tor be­reit­stellt, in Ent­wick­lung. Wird zur­zeit nur für den Re­fresh der Ober­flä­che be­nö­tigt. Durch ei­nen cast auf GpsDa­taE­di­tor er­schlie­ßen Sie sich GNavigia als Gan­zes! Aber das ha­ben Sie nicht von mir!
  • vo­id SetSta­tusBarText( string text) - Set den Haupt­text der Sta­tus­zei­le. Thread­si­cher.

Zwei wei­te­re Va­ria­blen iden­ti­fi­zie­ren den Cli­ent nach au­ßen, cs_cli­entNa­me ("cs_" steht für const sta­tic) und cs_cli­entID. Wäh­rend cs_cli­entNa­me auch ei­ne be­lie­bi­ge, mit Leer­zei­chen ver­se­he­nen Zei­chen­ket­te ent­hal­ten könn­te, emp­feh­le ich, cs_cli­entID mit ei­ner GUID zu be­le­gen. Sie kön­nen hier auch "Hu­go" schrei­ben, aber dann könn­te es Pro­ble­me ge­ben, wenn ein zwei­ter Cli­ent eben­falls die­se au­ßer­or­dent­lich ori­gi­nel­le ID wählt. Die Zu­ord­nung der ge­spei­cher­ten Da­ten zum Cli­ent er­folgt über die­sen Na­men! Dop­pel­te Ein­trä­ge füh­ren zu un­vor­her­seh­ba­ren Pro­ble­men. Al­so mer­ke: Nicht der Na­me muss glo­bal ein­deu­tig sein son­dern die Cli­ent-ID. Die ID ist ei­ne Zei­chen­ket­te, die ei­ne GUID re­prä­sen­tiert. Be­nut­zen Sie zur Er­zeu­gung ei­nes sol­chen Na­mens GUIDGEN.EXE und ent­fer­nen Sie die ge­schweif­ten Klam­mern!


Abb. 11: Klas­sen­va­ria­ble und Kon­struk­tor

Der Kon­struk­tor muss Pa­ra­me­ter­los sein. Es gibt auch kei­nen Grund, warum Sie ei­ne Über­la­dung schaf­fen soll­ten. Wür­den Sie dem Kon­struk­tor ei­nen for­ma­len Pa­ra­me­ter hin­zu­fü­gen, wür­de der Cli­ent igno­riert. Die­se Klas­se wird al­lein für die Kom­mu­ni­ka­ti­on be­nö­tigt und ei­ne In­stanz von Typ GNa­vi­giaGpsEle­ment­sC­li­ent wird nur von der Appli­ka­ti­on an­ge­legt. Wir nut­zen den Kon­struk­tor le­dig­lich zur Aus­ga­be ei­ner Nach­richt ins Log­fi­le.
Am En­de un­se­rer Be­mü­hun­gen füh­ren wir das Pro­gramm aus. Die DLL soll­te kor­rekt er­stellt und GNavigia.exe ge­st­ar­tet wer­den. Wenn Sie das Pro­gramm so­fort wie­der be­en­den, er­hal­ten Sie die fol­gen­de Aus­ga­be im Log­fi­le:


Abb. 12: Die Da­tei GNavigia.log

Der Quell­code - Teil II

Wir ha­ben un­ser Ziel, die Be­ar­bei­tung der Weg­punk­te, nicht aus dem Au­ge ver­lo­ren, auch wenn wir uns bis hier­her nur mit dem Rah­men be­schäf­tigt ha­ben, in den letzt­lich al­le Er­wei­te­run­gen von GNavigia ein­ge­bet­tet wer­den müs­sen. Wenn sie bis hier ge­kom­men sind, die Appli­ka­ti­on läuft und Sie das Log­fi­le so vor sich se­hen, wie zu­vor be­schrie­ben, ste­hen die Chan­cen gut für ei­ne Im­ple­men­tie­rung der Weg­punkt­be­rich­ti­gung.
Be­vor wir uns aber auf die Im­ple­men­tie­rung stür­zen, sei auf ei­nen wich­ti­gen Um­stand hin­ge­wie­sen, der leicht über­se­hen wer­den kann. Der Cli­ent ist im­mer Be­stand­teil der Appli­ka­ti­on und nicht Teil ei­nes be­stimm­ten Do­ku­ment­fens­ters. Da­her lie­fern An­fra­gen an den Ser­ver im­mer die Da­ten des ak­ti­ven Do­ku­ments. Ist kein Fens­ter ge­öff­net oder ent­hält das ak­ti­ve Do­ku­ment kei­ne pas­sen­den Da­ten, so wird ei­ne lee­re Men­ge zu­rück­ge­ge­ben. Hier­bei gilt: Lie­fert ei­ne Schnitt­stel­len­me­tho­de ei­ne Lis­te zu­rück, so ist das lee­re Er­geb­nis nie­mals null son­dern im­mer ei­ne Lis­te mit Count == 0. Die­sen Um­stand ha­ben wir uns in Ab­bil­dung 7 be­reits zu­nut­ze ge­macht.
Mit Ab­bil­dung 13 kom­men wir nun zum Kern der Sa­che, der Metho­de OnMe­nuItemClicked, die ich Ih­nen so lan­ge vor­ent­hal­ten ha­be. Das Me­nuItem mit Na­me WDK ver­zweigt zur Be­ar­bei­tung. Wir zäh­len die An­zahl der tat­säch­lich ge­än­der­ten Weg­punk­te in der Va­ria­blen count. Zu­gleich be­rück­sich­ti­gen wir, dass die Ana­ly­se des Da­tums, so wie der Emp­fän­ger es schreibt, nicht un­be­dingt von Stan­dard­me­tho­den er­kannt und feh­ler­frei in­ter­pre­tiert wird. Da­her klam­mern wir die Metho­de Da­teTi­me.Par­se in ei­nen try/catch Block. Zu­vor stel­len wir durch die Metho­de Sp­lit si­cher, die ge­gen da­te == null ge­si­chert ist, dass die for­ma­len Voraus­set­zun­gen für die In­ter­pre­ta­ti­on des Da­tums ge­ge­ben sind.
Feh­ler ge­ben wir ins Log­fi­le aus. Dort schau­en wir nach, wenn die An­zahl der be­han­del­ten Weg­punk­te von un­se­ren Er­war­tun­gen ab­weicht. Man könn­te auch noch die An­zahl der Feh­ler zäh­len und als drit­te Zei­le in der Mes­sa­geBox aus­ge­ben, aber das wür­de den Rah­men der Do­ku­men­ta­ti­on spren­gen. Zu­dem un­ter­su­chen wir vor­ab, ob nicht viel­leicht schon zu­vor Än­de­run­gen statt­ge­fun­den ha­ben. Da wir bei Er­folg das At­tri­but De­sc lö­schen, kön­nen wir Weg­punk­te aus der Be­hand­lung aus­schlie­ßen, für die De­sc un­gleich Cmt ist. Der mehr­fa­che Auf­ruf der Metho­de lie­fert dann das Er­geb­nis, dass die An­zahl der Än­de­run­gen 0 ist. Zu­letzt wei­sen wir Cmt den Wert des At­tri­buts Na­me zu, so­fern die­ser ei­ne gan­ze Zahl ist, al­so noch nicht von Hand ge­setzt wur­de.


Abb. 13: Die Metho­de OnMe­nuItemClicked

Die Kon­struk­ti­on m_g­psEle­mentSer­ver as IWin32Win­dow lie­fert das Fens­ter­hand­le der Appli­ka­ti­on, so­dass ein Dia­log oder ei­ne Mes­sa­geBox ein kor­rek­tes pa­rent win­dow mit­ge­ge­ben be­kom­men. Schlu­dern Sie hier nicht und be­nut­zen Sie die­se Mög­lich­keit!
Nun müs­sen wir nur noch schau­en, ob un­se­re Än­de­run­gen tat­säch­lich vor­ge­nom­men wor­den sind. Da­zu öff­nen wir den Rei­ter Weg­punk­te im Ver­wal­tungs­fens­ter der Appli­ka­ti­on und klap­pen ei­nen Weg­punkt auf. Das Er­geb­nis ist in Ab­bil­dung 14 fest­ge­hal­ten und ent­spricht un­se­ren Er­war­tun­gen. Zu­gleich ist die An­zei­ge im Haupt­fens­ter ak­tua­li­siert. Soll­te das ein­mal nicht ge­sche­hen, ru­fen Sie m_g­psEle­mentSer­ver.IGpsDa­taE­di­tor ab, prü­fen den Wert auf null und ru­fen dann die Metho­de GpsRe­draw an die­sem In­ter­face auf.
  Ach­tung: Spei­chern Sie kei­ne ver­gäng­li­chen Re­fe­ren­zen! Die ein­zi­ge Re­fe­renz, die im­mer gilt, ist die IGpsEle­mentSer­ver Re­fe­renz, die im Kon­struk­tor über­ge­ben wird. Al­le an­de­ren Re­fe­ren­zen hän­gen da­von ab, ob über­haupt ein Do­ku­ment exis­tiert und wel­ches das ist. Star­ten Sie kei­ne ei­ge­nen Threads! Das wird nicht funk­tio­nie­ren, wenn Ober­flä­chen­ele­men­te ins Spiel kom­men. Das Set­zen der Sta­tus­zei­le ist die ein­zi­ge Aus­nah­me. Dort wer­den frem­de Threads kor­rekt be­han­delt.


Abb. 14: Das Er­geb­nis am Bei­spiel von Weg­punkt 003

Nun bleibt zum Schluss nur noch zu er­wäh­nen, dass die Än­de­run­gen in den Tie­fen von GNavigia statt­ge­fun­den ha­ben und da­her fes­ter Be­stand­teil des Un­do/Redo-Mecha­nis­mus sind. Um Dar­stel­lungs­flä­che zu spa­ren wur­de die Lis­te in Ab­bil­dung 15, die er­war­tungs­ge­mäß 9 Weg­punk­te um­fasst, zu­sam­men­ge­scho­ben. Die Lis­te liest sich et­was an­ders als an­de­re Wie­der­her­stel­len-Lis­ten: Es wird in je­der Zei­le dar­ge­stellt, wel­che Ak­ti­on ein Un­do aus­löst. In der obers­ten Zei­le er­kennt man, dass das auf null ge­setz­te At­tri­but De­sc mit dem Wert des ur­sprüng­li­chen Da­tums be­legt wer­den wür­de. Die Lis­te wird rie­sig, wenn Sie sehr vie­le Da­ten auf ein­mal än­dern. Ei­ne Zu­sam­men­fas­sung von Ak­tio­nen ist zur­zeit noch nicht mög­lich.


Abb. 15:  Die Undo-Lis­te nach den Än­de­run­gen