WPF Programátorská kaklulačka - Design a CodeBehind
Tento tutoriál je zameraný na praktické využitie nadobudnutých vedomostí z C# .NET a WPF z tunajších tutoriálov. Pridáva tiež pár zatiaľ nezmiňovaných vecí a nie celkom typických riešenia, prípadne alternatív k podobným postupom v iných tutoriáloch, ako napríklad použitie "univerzálneho" typu dynamic.
Nie je zameraný na XAML ako taký, ten je popísaný v predchádzajúcich dieloch dosť obšírne. Na konci budete mať funkčnú aplikáciu, ktorá nájde uplatnenie hlavne u programátorov jednočipových MCU.
Zadania
Zadaním je programátorská kalkulačka, ktorá by mala spĺňať tieto podmienky, ktoré mi chýba u tradičných kalkulačiek:
- Stále on top - neschovávať sa pri každej voľbe iného okna, čo mne u bežných kalkulačiek slušne povedané "irituje"
- Zaberať čo najmenej miesta - Což vychádza z prvej podmienky, aby pokiaľ možno neprekážala. Na väčšine IDE (editorov) sa nejaké to miesto, kam ju strčiť, nájde.
- Zobrazovať každú hodnotu v decimálnom, hexadecimáním a binárnom tvaru naraz
- Prístup k akejkoľvek pozícii v zadanom čísle pre jeho zmenu
- V binárne a hexa sekcii oddelené jednotlivé bajty, doplnené o patričný počet núl (0F, 0001001) - pre prehľadnosť
- Použitie najbežnejších aritmetických a logických operácií
- Výber dátového typu, na ktorom sa budú operácie vykonávať
Dizajn
Je minimalistiký a rýdzo účelový, všetko je podriadené podmienke čo najmenšieho okna.
Aplikácia sa skladá z deviatich textbox, troch buttonov, troch textBlocků, ComboBox a jednej čiary, ktorú reprezentuje rectangle a toť všetko. Oproti štandardným kalkulačkám je to vlastne len ich displej, všetko potrebné je predsa na klávesnici, tak na čo tam pchať veľa tlačidiel?
Všetky prvky majú svoj chlievik v gridu a ich layout je vzťahujúci práve k nemu. To a zároveň skutočnosť, že takmer všetky prvky vrátane okna a gridu majú svoje property šírky a výšky až na výnimky nastavené na auto, zabezpečí, že okno rastie rovnomerne s textom v boxoch. Textbox majú len nastavenú minimálnu šírku. Bez textu sú moc úzke a okrem toho, že to zle vyzerá, sa do nich taky zle triafa
Veľkosť okna je tak daná hlavne veľkosťou fontu zadaného v properties okna. Slepúch, ako som ja, si ho môžu zväčšiť.
Ďalšie úsporu miesta zabezpečí to, že sa zbavíme rámčeku okna. To docielime nastavením property WindowStyle na none alebo v XAML pridaním do riadku okna WindowStyle = "None". Tým sa ale pripravíme o akúkoľvek manipuláciu s oknom. Keď ho raz otvoríme, tak už sa ho nezbavíme. Leda v správcovi úloh - čo s tým? Musíme ovládacie prvky preniesť na okno samotnej - ale o tom až v ďalšom diele.
Implementácia
Prvý podmienku zabezpečíme nasledovne: v XAML do koreňového elementu okna zadáme
Topmost="True"
Alebo jednoducho zaškrtneme checkbox pri príslušnej vlastnosti v properties okna. Odstránením rámčeku sa zdá zbytočné zadávať oknu titulok a ikonu, ale pretože sa zobrazujú na taksbaru, tak je tam dáme.
Pre zobrazenie výsledku by som normálne použil textBlocky, ale pretože majú trochu iný dizajn a text v okienku zobrazujú trochu posunutý proti textbox, tak som z dôvodu zjednotenia vzhľadu využil textbox a vlastnosť "focusable" nastavil na false, aby sa do nich nedalo písať.
Na spodnej strane sú umiestnené tri buttony - dva pre Minimize a close a jeden, ktorý bude otvárať okno s nápovedou. Buttony nemajú žiadny text, len sú ako pozadie použité ikony stiahnuté z IconFinder a ako jediné elementy majú dané pevné rozmery.
Code Behind
Deklarácie a konštruktor
Deklarujeme / definujeme kolekciu znakov zastupujúcich jednotlivé operátormi. Tú potom odovzdáme ako ItemsSource ComboBox pre výber operácie a zriadime inštancie tried zaisťujúcich samotný prevod a výpočet:
Prevod prevody; Vypocet vypocty; //itemy comboBoxu List<string> operatory = new List<string>() { "+", "-", "x", "/", "%", "&", "|", "^", "~", "<<", ">>" }; //konstruktor public MainWindow() { InitializeComponent(); vypocty = new Vypocet(); prevody = new Prevod(vypocty); comboBoxOperator.ItemsSource = operatory; comboBoxOperator.FontSize = 10; ComboBoxTyp.ItemsSource = prevody.dateTypeDictionary.Keys; ComboBoxTyp.FontSize = 10; }
Začnem asi najzložitejšie metódou:
private void textBox_TextChanged(object sender, TextChangedEventArgs e) { TextBox tb = sender as TextBox; string s = tb.Text; tb.CaretIndex = tb.Text.Length; if (tb.IsFocused) { if (s != "") { try { prevody.prevod(s, tb.TabIndex); } catch(Exception ex) { MessageBox.Show(ex.Message); } vypocty.Result = 0; showResult(); wiewDec(); wiewHex(); wiewBin(); } else { if (tb.TabIndex % 2 == 0) clearA(); else clearB(); clearRes(); } } }
Je to metóda pre obsluhu udalosti textChanged a bude spoločná pre všetky textbox, pretože inak by tam bolo šesť metód, ktoré robia to isté - volajú metódu pre prevod reťazca na hodnotu premennej.
Na druhú stranu ale musíme vedieť, s ktorým textbox práve pracujeme, takže si na začiatku zriadime jeho inštanciu. Keďže sa ale sender prezentuje ako object, musíme ho na textbox pretypovať:
TextBox tb = sender as TextBox;
Pre identifikáciu použijeme jeho tabindex. Zadáme v properties alebo XAML, ako je komu libo, hodnoty od 0 do 5 pre jednotlivé textbox podľa nasledujúcej schémy:
A pretože v tejto metóde meníme texty všetkých šiestich operandových textbox, tak sa tiež šesťkrát zavolá. Ide vlastne o rekurzívne volanie metódy (volá aj keď nepriamo sama seba), čo by narobilo pekný zmätok a tak musíme zabezpečiť, aby sa jej kód vykonal len na textbox, s ktorým pracujeme. To zistíme vlastností:
if (tb.IsFocused)
Po teste, či nie je reťazec prázdny (po mazanie Backspace) sa vykoná samotný prevod, ktorý je v bloku try-catch. To preto, aby sa niekto nepokúšal prevádzať treba 354 na byte. Ďalej zobrazíme hodnotu vo všetkých požadovaných tvaroch (dec, hex, bin)
if (s != "") { try { prevody.prevod(s, tb.TabIndex); } catch(Exception ex) { MessageBox.Show(ex.Message); } vypocty.Result = 0; showResult(); wiewDec(); wiewHex(); wiewBin(); }
Ak je prázdny, vymaže sa príslušný riadok s operandy. Ktorý riadok to je so zistí práve z tabindex. Horný riadok má párne a spodná nepárne, čo zistíme jednoducho operátorom modulo (nepárne budú mať zvyšok).
else { if (tb.TabIndex % 2 == 0) clearA(); else clearB(); clearRes(); }
Ďalej budeme pokračovať metódami pre obsluhou vstupov z HID - teda myši a klávesnice. Vzniká tu nutnosť filtrovať znaky prichádzajúce z klávesnice a to nie na udalosť textChanged, to už je neskoro. Musíme filtrovať v metóde obsluhy udalosti textBox_PreviewTextInput, teda kým nie je znak do textu zapracovaný.
U decimálneho zobrazenie je to jednoduché. Potrebujeme vedieť, či sa jedná o číslicu. To sa dozvieme za pomoci:
char.IsDigit
U hex hodnoty potrebujeme pridať ešte znaky AF (af). K tomu slúži zriadená "tabuľka" string hexValue = "0123456789ABCDEFabcdef";
Rovnako tak to bude fungovať aj pre binárne hodnoty.
// znaky 0,1,2,3,4,5,6,7,8,9,0 private void textBoxDec_PreviewTextInput(object sender, TextCompositionEventArgs e) { if (!char.IsDigit(e.Text, 0)) e.Handled = true; } // znaky 0,1,2,3,4,5,6,7,8,9,0,A,B,C,D,E,F private void textBoxHex_PreviewTextInput(object sender, TextCompositionEventArgs e) { string hexValue = "0123456789ABCDEFabcdef"; if (!hexValue.Contains(e.Text)) e.Handled = true; } // znaky 0,1 private void textBoxBin_PreviewTextInput(object sender, TextCompositionEventArgs e) { string binValue = "01"; if (!binValue.Contains(e.Text)) e.Handled = true; }
V ďalšom diele dokončíme Code Behind a pozrieme sa na triedy vykonávajúce samotné prevody a operácie. Zdrojový kód vrátane ikoniek je priložený.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 161x (38.1 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#