Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

WPF Programátorská kalkulačka - Dokončení

V prvním díle jsem začal C# část kódu programátorské kalkulačku v C# .NET WPF. Dnes ji dokončíme.

I když by šel průběžně se zadávanými hodnotami zobrazovat i výsledek, nepřipadá mi to příliš dobré řešení a proto se výsledek ukáže až po stisku enteru na klávesnici. Také je potřeba kalkulačku zresetovat. K tomu stejně jako u jiných aplikací využijeme klávesu esc

Každý stisk klávesy, stejně jako pohyb myši, nebo změnu stavu tlačítka na ní, vyvolá příslušnou událost. My budeme obsluhovat událost "KeyDown". V properties hlavního okna si v eventech dvojklikem na "KeyDown" definujeme metodu pro obsluhu stisklé klávesy:

private void Window_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        try
        {
            vypocty.vypocet();
            showResult();
        }

        catch(Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    else if (e.Key == Key.Escape)
    {
        clearAll();
    }
}

Myslím, že z kódu je jasné, jak zjistíme která klávesa byla stisklá a co se dál provede. Při nezdařeném výpočtu zachytíme výjimku a aktivujeme MessageBox s příslušnou hláškou.

Dále se dostáváme k ovládání samotného okna:

private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    while (Mouse.LeftButton == MouseButtonState.Pressed)
    {
        DragMove();
    }
}

//obsluha buttonu

private void buttonMinimize_Click(object sender, RoutedEventArgs e)
{
   this.WindowState = WindowState.Minimized;
}

private void buttonClose_Click(object sender, RoutedEventArgs e)
{
    this.Close();
}

private void buttonHelp_Click(object sender, RoutedEventArgs e)
{
    WindowHelp windowHelp = new WindowHelp();
    windowHelp.Show();
}

Z kódu je opět nad slunce jasné co metody událostí "Click" jednotlivých buttonů dělají. Snad jen k přesunu okna dodám, že je realizovaný metodou DragMove();, tedy při stisku a držení levého tlačítka myši.

Nakonec zbývá výběr typu a operátoru z comboBoxů. Metody si opět připravíme v properties dvojklikem na event SelectionChanged. Na výběru typu není nic zvláštního, jen se tu poprvé setkáme se slovníkem:

private void ComboBoxTyp_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    prevody.Typ = ComboBoxTyp.SelectedItem.ToString();
    vypocty.PocetBytu = prevody.dateTypeDictionary[prevody.Typ];
}

Výběr operátoru je trochu složitější, protože se některé logické operace provádějí jen na jednom operandu a tak je nutné zakázat/povolit přístup k tomu druhému.

Třídy pro zpracování dat

Třída Vypocet

Je velmi jednoduchá. Má pět vlastností a jedinou metodu, která provádí samotné aritmetické a logické operace:

class Vypocet
{
    public dynamic A_val { get; set; }
    public dynamic B_val { get; set; }
    public dynamic Result { get; set; }
    public string Operator;
    public int PocetBytu { get; set;}

    public Vypocet()
    {
        A_val = 0;
        B_val = 0;
        Result = 0;
    }

    internal void vypocet()
    {
        switch (Operator)
        {
            case "+": Result = A_val + B_val; break;
            case "-": Result = A_val - B_val; break;
            case "x": Result = A_val * B_val; break;
            case "/": Result = A_val / B_val; break;
            case "%": Result = A_val % B_val; break;
            case "&": Result = A_val & B_val; break;
            case "|": Result = A_val | B_val; break;
            case "^": Result = A_val ^ B_val; break;
            case "~": Result = ~A_val; break;
            case "<<": Result = A_val << B_val; break;
            case ">>": Result = A_val >> B_val; break;
        }

        double x=Math.Pow(2,(double)(PocetBytu*8))-1;
         if(Result> x)
        {
            throw new Exception("při operaci došlo k přetečení");
        }
    }
}

Co je na ní ovšem zajímavého je použití datového typu dynamic. Je to takový universální typ, který může zastoupit kterýkoliv, v našem případě hodnotový typ. Dynamic se navíc muže za běhu programu měnit, což není u běžných typů možné.

Musel jsem se k němu uchýlit z důvodu splnění poslední podmínky z úvodního článku - výběr typu, na kterém budou operace prováděny. Ovšem jako vždy je jeho univerzálnost je vykoupena určitým nepohodlím, nefunguje na něm intelisense, takže co si s ním můžete dovolit je potřeba hledat v dokumentaci a navíc na něm nejdou použít operátory jako sizeof, nebo kontrola přetečení za pomoci checked, což by se tu zrovna hodilo. Proto jsem musel přistoupit k poměrně krkolomnému zjištění max. hodnoty aktuálního typu (dynamic.MaxValue celkem logicky nefunguje) a porovnání s výsledkem operace s případným vyhozením výjimky při přetečení:

double x=Math.Pow(2,(double)(PocetBytu*8))-1;
if(Result> x)
{
   throw new Exception("při operaci došlo k přetečení");
}

Třída Prevod

Je už trochu složitější a to hlavně kvůli požadavku dělení hex a bin hodnot na jednotlivé bajty pomocí mezer v textu. Nejdříve se musí typ rozsekat na jednotlivé bajty tím, že definujeme pole bajtů o velikosti daného typu. Jak ji ale zjistit, když sizeof(dynamic) nefunguje? Nezjistíme ji nijak, musí se definovat na tvrdo v nějaké tabulce a k tomuto účelu bude nejvhodnější Slovník, kde budou názvy typů sloužit jako klíče a hodnoty budou příslušné počty bajtů. Navíc zabijeme více much jednou ranou, protože kolekci klíčů předáme comboBoxu pro výběr typu jako "ItemsSource" a hodnoty použijeme ve třídě Vypocet pro kontrolu přetečení.

Jako první je veřejná metoda, která je volaná z metody textBox_TextChanged a rozlišuje pomocí v parametru předaného tabIndexu ve kterém textBoxu zrovna měníme hodnotu. Podle toho předá parametry ve formě textu z textBoxu a číselné soustavy (2,10,16) privátní metodě, která provede samotný převod na zvolený typ. Ještě před tím projede foreachem text a odstraní z něj mezery.

Try-catch blokem je ošetřeno zadání většího čísla, než je max. hodnota daného typu:

private dynamic prevedTyp(string s,int soustava)
{
    dynamic ret=null;
    //odstraneni mezer z textu
    foreach (char c in s)
    {
        if (c == ' ') s = s.Remove(s.IndexOf(" "), 1);
    }
    try
    {
        switch (Typ)
        {
            case "byte": ret = Convert.ToByte(s, soustava); break;
            case "sbyte": ret = Convert.ToSByte(s, soustava); break;
            case "Int16": ret = Convert.ToInt16(s, soustava); break;
            case "Int32": ret = Convert.ToInt32(s, soustava); break;
            case "UInt16": ret = Convert.ToUInt16(s, soustava); break;
            case "UInt32": ret = Convert.ToUInt32(s, soustava); break;
        }
    }
    catch
    {
        throw new Exception("číslo je větší než max. hodnota zvoleného typu");
    }
    return ret;
}

Další je metoda pro konverzi opačným směrem, tedy z datového typu na řetězec znaků. V ní nejdříve zjistíme ze slovníku, kolik má bytů a podle toho definujeme pole bajtů:

int pocetBytu = dateTypeDictionary[Typ];
byte[] tmp = new byte[pocetBytu];

Bajty jeden po druhém převedeme na znaky a spojíme do jednoho řetězce. Současně doplníme mezerami pomocí metody PadLeft(8, '0'), (první parametr určuje kolik míst se bude doplňovat a druhý čím se bude doplňovat). Nuly doplňujeme proto, aby byl vidět celý bajt, je to přehlednější.

for (int i = 0; i < tmp.Length; i++) { tmp[i] = (byte)val; val >>= 8; }

for (int j = tmp.Length - 1; j >= 0; j--)
{
   if (tmp[j] != 0)
   {
       if(soustava==2)
       {
           s += Convert.ToString(tmp[j], soustava).PadLeft(8, '0');
           if(j!=0)s+="  ";
       }
       else if (soustava == 16)
       {
           s += string.Format(string.Format("{0:X}", tmp[j])).PadLeft(2, '0');
           if (j != 0) s += " ";
       }
   }
}

Možná si řeknete, proč jsem použil pro bin. číslo třídu Convert a pro hex. číslo string.Format. Za prvé pro ilustraci, že máme více možností, ale hlavně proto, že třída Convert převádí znaky a-f na malá písmena a já mám raději velká. A formátování stringu mi dovolí obojí.

Projekt obsahuje ještě "Help" okno, které je ale primitivní (obsahuje jeden textBox), že ho zde nemá cenu popisovat. Pro zájemce přikládám celý projekt ve VS2010.


 

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 266x (591.6 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Všechny články v sekci
Zdrojákoviště C# .NET - Okenní aplikace WPF
Článek pro vás napsal ostrozan
Avatar
Uživatelské hodnocení:
5 hlasů
Autor se věnuje především embedded systémům (mikrokontrolery) a vývoji jejich GUI v C# WPF , Java.
Aktivity