IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

Dráhy planét

K čomu aplikácia slúži?

Program počíta a následne vykreslí dráhy planét (ich relatívna vzdialenosti vzhľadom ku zvolenej "stredovej" planéte). Navolené planéty a nastavenia je možné uložiť do súboru a zo súboru zase načítať. Program podporuje exportovanie obrázku vo formáte .png alebo .bmp.

Prečo práve dráhy planét?

Pri hodinách astronómie v rámci fyziky sme tieto dráhy museli pracne rysovať (trvalo to niekoľko hodín). Na popud kamaráta som preto začal pracovať na programe, ktorý mal prácu zjednodušiť. Algoritmus pre zistenie pozície planéty v danom čase som vymyslel už vtedy pri hodine a nie je nijako zložitý. Netušil som však, že sa nakoniec aplikácia so všetkou omáčkou okolo (návrh aplikácie, GUI podľa mojich predstáv, správne vykreslenie, flexibilita programu, ukladanie do súboru atď.) Rozrastie v môj zatiaľ asi najväčší projekt a bude trvať desiatky hodín ho naprogramovať.

Poznámka: Planéta je pre jednoduchosť myslené akékoľvek teleso, ktoré obieha okolo iného.

Čo som sa naučil?

Chcel by som sa podeliť o niekoľko zo skúseností, ktoré som počas vývoja získal. Dúfam, že pomôžu vyriešiť problémy podobné tým, s ktorými som sa počas vývoja stretol.

Krivka spájajúca body

Problém

Ako spojiť body značiace dráhu planéty plynulou krivkou? Nechcel som body spájať rovnými úsečkami - nevyzeralo by to dobre a pre trochu hladšie krivku by bolo potrebné vygenerovať veľmi veľa bodov. Čo s tým?

Riešenie

Zistil som, že napísať vykreslenie takéto krivky pomocou Beziérových kriviek vyzerá trochu zložito. Nakoniec som našiel, že knižnice Windows Forms už takúto funkciu obsahujú. Konkrétne metódu Graphics.DrawCurve(Pen pen, Point[] points); Rozhodol som sa teda aplikáciu kompletne prerobiť z WPF do WinForms.

Prispôsobenie šírky kontroliek scrollbaru

Problém

Máme FlowLayoutPanel s FlowDirection = TopDown. Chceme, aby sa pri nedostatku miesta pre kontrolky objavil scrollbar, nastavíme teda AutoScroll na True a WrapContents na False. Tu nastáva problém. Scrollbar sa síce objaví podľa očakávaní, ale zaberie miesto v paneli. Tým pádom sa kontrolky nezmestia vodorovne a objaví sa aj vodorovný scrollbar. To však nie je požadované správanie. Lepšie by bolo, keby sa kontrolky vodorovne o trochu zmenšili a urobili tak miesto pre scrollbar. Neprišiel som ale na to, ako tohto správania dosiahnuť bez pomoci vlastného kódu na pozadí. Prišiel som ale na iné riešenie.

Riešenie

Riešením je nepoužívať FlowLayoutPanel, ale klasický Panel. Panelu sa nastaví AutoScroll na True a kontrolkám vnútri Dock = Top. Jednoduché, že? Keď sa teraz kontrolky na panel nezmestia, objaví sa zvislý scrollbar a kontrolky sa trochu zmenší, aby mu urobili miesto.

Skrytie položiek v dropdown

Problém

Planéty v programe majú vlastnosť Parent, ktorá udáva, okolo aké planéty má daná planéta obiehať. Výber planéty som zo začiatku riešil ComboBox (DropDownStyle = DropDownList), ktorý bol binding napojený na zoznam všetkých planét. Problémom je v tom, že ako rodičovskú planétu nemôžem nastaviť planétu samotnú ani žiadnu z jej "detí". Tie sa teda nesmie objaviť ani v ComboBox. Otázkou je, ako je v combobox skryť. Prvé pokusy viedli cez udalosť Binding.Format. Neboli ale úspešné. Buď by sa nevhodné planéty odobrali aj z pôvodného BindingListu (čo samozrejme nechceme), alebo by nefungoval binding, pretože sme položky prekopírovali do novej kolekcie. Ďalšou možnosťou, ktorú som našiel bolo skryť nežiadané položky v ComboBox. To má ale hneď niekoľko háčikov. Po prvé, je potrebné prepísať ComboBox tak, aby skryl nechcené položky. To zahŕňa vytvorenie vlastnej kontrolky a prepísanie logiky vykresľovanie a pozicovanie, pričom sa navyše môžu vyskytnúť nové problémy. Po druhé, užívatelia vždy nájdu spôsob, ako niečo rozbiť. Napríklad, ak by sme formulár ovládali pomocou klávesnice, môžeme označiť aj skryté položky (ktoré tam stále sú, len nie sú vidieť). Po tretie, bolo by treba nejako upraviť program tak, aby bolo možné aktualizovať informáciu o tom, či má byť položka skrytá - znamenalo by to zasahovať do už existujúceho kódu a veľmi ho znepřehlednit.

Dlho som hľadal a vymýšľal ďalšie riešenia, ale na nič funkčného som neprišiel. Nakoniec som sa rozhodol, že sa pokúsim nájsť iný spôsob výberu rodičovskej planéty. Vymyslel som, že po kliknutí na tlačidlo sa zobrazí dialóg s ComboBox na výber planéty, ktorý pred zobrazením načíta planéty, ktorá aktuálne môžu byť "rodičom". V tom sa mi rozsvietilo. Veď vôbec nebolo potrebné vytvárať nový dialóg, ktorý rovnako obsahoval len pôvodné ComboBox.

Riešenie

Riešením je aktualizovať ComboBox v udalosti Enter, ktorá sa spustí, keď sa daná kontrolka stane aktívnou kontrolkou formulára. Tu jednoducho získame validný položky (s pomocou LINQ je to otázkou jedného riadku) a zobrazíme je užívateľovi. Dropdown sa tak aktualizuje vždy, keď užívateľ bude chcieť vybrať novú položku, či už myšou alebo klávesnicou.

Dodatok: Rovnako som na niečo zabudol. Keď používateľ nabehne myšou na ComboBox a scrolluje, môže zmeniť označenú položku bez spustenia udalosti Enter. Na riešenie sa môžete pozrieť do zdrojových kódov (udalosti MouseEnter a MouseWheel).

Prázdna položka v dropdown

Problém

Keď planéta neobieha okolo inej planéty (ako napríklad Slnko), je jej vlastnosť Parent nastavená na hodnotu null. Do combobox by teda bolo vhodné pridať prázdnu položku, ktorá by reprezentovala hodnotu null. Do kolekcie s planétami ani do dropdown hodnotu null pridať nemôžeme, keďže všetky prázdne pozície v zozname majú hodnotu "null" a nič by sa nám nezmenilo.

Riešenie

Riešením je v metóde, ktorá filtruje vhodné planéty pridať položku s textom napríklad "<None>" (môže byť aj prázdny reťazec). Pri pretypovanie označené položky v udalosti SelectedValueChanged späť na pôvodnú typ (v tomto prípade Planet) nepoužiť notáciu (Planet) ComboBox.SelectedItem, ale ComboBox.SelectedItem as Planet. Tým nám pretypovanie stringu na planétu nevyhodí výnimku, ale jednoducho bude null - presne ako sme chceli.

Automatické mierka, vycentrovanie a zoom

Problém

Vzdialenosti medzi planétami môžu byť veľmi odlišné a pri stále rovnakej mierke môžeme niekedy vidieť jedinú planétu cez celú obrazovku a inokedy zas celú slnečnú sústavu iba ako bodku. Bolo by dobré meradlo automaticky prispôsobovať.

Riešenie

WinForms nám túto prácu značne zjednoduší svojimi vstavanými funkciami. Najprv presunieme bod [0; 0] doprostred: g.TranslateTransform(.VisibleClipBounds.Width / 2, g.VisibleClipBounds.Height / 2). Pre zmenu mierky stačí jednoducho funkciu Graphics.ScaleTransform(float sx, float sy) odovzdať mierka v osi xaya pri vykresľovaní sa na každý bod automaticky aplikuje transformácie. Zostáva nám zistiť správne meradlo. To získame nasledovne: zoom * velikost plátna / maximální velikost vykreslovaného obrázku.

Obrázkové fonty

Problém

Pôvodne som v aplikácii pre ikonky používal obrázkové fonty ako sú napríklad Wingdings3. Zistil som ale, že na niektorých zariadeniach nie je font nainštalovaný a miesto ikoniek sa zobrazujú "obdĺžničky".

Riešenie

Trochu zložitejšie cestou s použitím systémových knižníc možno síce fonty importovať, mohli by ale vzniknúť problémy s vlastníckymi právami. Urobiť (ešte krajšie : P ) Ikonky v Inkscape a nastaviť ich ako obrázok na pozadí kontrolky bolo otázkou niekoľkých minút a ušetril som si tak prácu aj nezmyselné otázky ohľadom copyrightu.

Otváranie súborov pomocou aplikácie

Problém

Bolo by dobré, keby aplikácia podporovala otváranie súborov i pomocou "Otvoriť v programe" alebo pretiahnutím súboru na ikonku aplikácie.

Riešenie

Riešenie je veľmi jednoduché, nepotreboval som internet a bolo to presne tak, ako som si to predstavoval. V súbore Program.cs pridáte do static void Main() argument string[] args (tak, ako to býva v konzolovej aplikácii). Získate tak argumenty, s ktorými bola aplikácia spustená (v tomto prípade bude pole obsahovať jednotlivé cesty súborov).

Záverom

Rád by som sa vývoji aplikácie venoval ešte ďalej, ale neviem či sa k tomu ešte dostanem. So všetkým som odhadom strávil vývojom niečo pod 100 hodín (= cca 10 000 Sk), takže budem rád za vašu podporu (čo i len milý komentár že aplikácia dobre slúži :-) ). V budúcej verzii by sa mohol napríklad objaviť progressbar, ktorý ukazuje aktuálny stav vykresľovanie.

Aj keď aplikácia svoj pôvodný účel ušetrenie práce vôbec nesplnila, som veľmi rád, že som sa do toho pustil. Najvzácnejšie na tom sú totiž skúsenosti, ktoré som vďaka tomu získal.

Ako vždy, v komentároch sa budem tešiť na pripomienky, prípadné hlásenia bugov a diskusia o iných možných riešeniach, ktorá vás napadnú.


Galéria


 

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 66x (458.72 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Všetky články v sekcii
Zdrojákoviště C # .NET - Okenné aplikácie Windows Forms
Program pre vás napísal David Dostal
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor programuje primárně v C#.Net a Ruby. Dále se zajímá o webové technologie (HTML5, CSS3, ES6) a funkcionální programování (F#). Rád se učí nové věci.
Aktivity