WPF Programátorská kaklulačka - Design a CodeBehind
Tento tutoriál je zaměřený na praktické využití nabytých znalostí z C#.NET a WPF ze zdejších tutoriálů. Přidává též pár zatím nezmiňovaných věcí a ne zcela typických řešení, popřípadě alternativ k podobným postupům v jiných tutoriálech, jako například užití "univerzálního" typu dynamic.
Není zaměřený na XAML jako takový, ten je popsán v předchozích dílech dost obšírně. Na konci budete mít funkční aplikaci, která najde uplatnění hlavně u programátorů jednočipových MCU.
Zadání
Zadáním je programátorská kalkulačka, která by měla splňovat tyto podmínky, které mi chybí u tradičních kalkulaček:
- Stále on top - Neschovávat se při každé volbě jiného okna, což mně u běžných kalkulaček slušně řečeno "irituje"
- Zabírat co nejméně místa - Což vychází z první podmínky, aby pokud možno nepřekážela. Na většině IDE (editorů) se nějaké to místo, kam ji strčit, najde.
- Zobrazovat každou hodnotu v decimálním, hexadecimáním a binárním tvaru najednou
- Přístup k jakékoli pozici v zadaném čísle pro jeho změnu
- V binární a hexa sekci oddělené jednotlivé bajty, doplněné o patřičný počet nul (0F,0001001) - pro přehlednost
- Použití nejběžnějších aritmetických a logických operací
- Výběr datového typu, na kterém se budou operace provádět
Design
Je minimalistiký a ryze účelový, vše je podřízeno podmínce co nejmenšího okna.
Aplikace se skládá z devíti textBoxů, tří buttonů, tří textBlocků, comboBoxu a jedné čáry, kterou reprezentuje rectangle a toť vše. Oproti standardním kalkulačkám je to vlastně jen jejich displej, vše potřebné je přeci na klávesnici, tak na co tam cpát spoustu tlačítek?
Všechny prvky mají svůj chlívek v gridu a jejich layout je vztažený právě k němu. To a zároveň skutečnost, že téměř všechny prvky včetně okna a gridu mají své property šířky a výšky až na výjimky nastaveny na auto, zajistí, že okno roste rovnoměrně s textem v boxech. TextBoxy mají jen nastavenou minimální šířku. Bez textu jsou moc úzké a kromě toho, že to špatně vypadá, se do nich taky špatně strefuje
Velikost okna je tak daná hlavně velikostí fontu zadaného v properties okna. Slepýši, jako jsem já, si ho můžou zvětšit.
Další úsporu místa zajistí to, že se zbavíme rámečku okna. Toho docílíme nastavením property WindowStyle na none nebo v XAMLU přidáním do řádku okna WindowStyle="None". Tím se ale připravíme o jakoukoli manipulaci s oknem. Když ho jednou otevřeme, tak už se ho nezbavíme. Leda ve správci úloh - co s tím? Musíme ovládací prvky přenést na okno samotné - ale o tom až v dalším díle.
Implementace
První podmínku zajistíme následovně: v XAMLu do kořenového elementu okna zadáme
Topmost="True"
Nebo prostě zaškrtneme checkBox u příslušné vlastnosti v properties okna. Odstraněním rámečku se zdá zbytečné zadávat oknu titulek a ikonu, ale protože se zobrazují na taksbaru, tak je tam dáme.
Pro zobrazení výsledku bych normálně použil textBlocky, ale protože mají trochu jiný design a text v okénku zobrazují trochu posunutý proti textBoxu, tak jsem z důvodu sjednocení vzhledu využil textBoxy a vlastnost "focusable" nastavil na false, aby se do nich nedalo psát.
Na spodní straně jsou umístěny tři buttony - dva pro minimize a close a jeden, který bude otvírat okno s nápovědou. Buttony nemají žádný text, jen jsou jako background použité ikony stažené z iconfinderu a jako jediné elementy mají dané pevné rozměry.
Code Behind
Deklarace a konstruktor
Deklarujeme/definujeme kolekci znaků zastupujících jednotlivé operátory. Tu pak předáme jako ItemsSource comboBoxu pro výběr operace a zřídíme instance tříd zajišťujících samotný převod 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čnu asi nejsložitější metodou:
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 metoda pro obsluhu události textChanged a bude společná pro všechny textBoxy, protože jinak by tam bylo šest metod, které dělají to samé - volají metodu pro převod řetězce na hodnotu proměnné.
Na druhou stranu ale musíme vědět, se kterým textBoxem právě pracujeme, takže si na začátku zřídíme jeho instanci. Jelikož se ale sender prezentuje jako object, musíme ho na textBox přetypovat:
TextBox tb = sender as TextBox;
Pro identifikaci použijeme jeho tabIndex. Zadáme v properties nebo XAMLU, jak je komu libo, hodnoty od 0 do 5 pro jednotlivé textBoxy podle následujícího schématu:
A protože v této metodě měníme texty všech šesti operandových textBoxů, tak se také šestkrát zavolá. Jde vlastně o rekurzivní volání metody (volá i když nepřímo sama sebe), což by nadělalo pěkný zmatek a tak musíme zajistit, aby se její kód provedl jen na textBoxu, se kterým pracujeme. To zjistíme vlastností:
if (tb.IsFocused)
Po testu, jestli není řetězec prázdný (po mazání backspacem) se provede samotný převod, který je v bloku try-catch. To proto, aby se někdo nepokoušel převádět třeba 354 na byte. Dále zobrazíme hodnotu ve všech požadovaných tvarech (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(); }
Pokud je prázdný, vymaže se příslušný řádek s operandy. Který řádek to je se zjistí právě z tabIndexu. Horní řádek má sudé a spodní liché, což zjistíme jednoduše operátorem modulo (liché budou mít zbytek).
else { if (tb.TabIndex % 2 == 0) clearA(); else clearB(); clearRes(); }
Dále budeme pokračovat metodami pro obsluhou vstupů z HID - tedy myši a klávesnice. Vzniká tu nutnost filtrovat znaky přicházející z klávesnice a to nikoliv na událost textChanged, to už je pozdě. Musíme filtrovat v metodě obsluhy události textBox_PreviewTextInput, tedy dokud není znak do textu zapracován.
U decimálního zobrazení je to jednoduché. Potřebujeme vědět, jestli se jedná o číslici. To se dozvíme za pomoci :
char.IsDigit
U hex hodnoty potřebujeme přidat ještě znaky A-F (a-f). K tomu slouží zřízená "tabulka" string hexValue = "0123456789ABCDEFabcdef";
Stejně tak to bude fungovat i pro binární 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 dalším díle dokončíme Code Behind a podíváme se na třídy provádějící samotné převody a operace. Zdrojový kód včetně ikonek je přiložen.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 161x (38.1 kB)
Aplikace je včetně zdrojových kódů v jazyce C#