Lekce 5 - Code Behind v C# .NET WPF a dokončení kalkulačky

C# .NET Formuláře WPF Code Behind v C# .NET WPF a dokončení kalkulačky

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Návrh formuláře pro kalkulačku v C# .NET WPF, jsme nakódovali formulář pro jednoduchou kalkulačku. Dnes v C# .NET tutoriálu do aplikace přidáme jednoduchou logiku a na konci si vysvětlíme jak WPF uvnitř funguje.

Code Behind

Prezentační část aplikace je napsaná v XAMLu. Ten nám však samozřejmě nestačí a proto má každé okno kromě XAML kódu ještě tzv. Code Behind (kód na pozadí), který obsahuje volání logiky aplikace. Do tohoto kódu se z designeru přesuneme pravým kliknutím myši a zvolením možnosti View Code. Alternativně můžeme používat klávesové zkratky Ctrl + Alt + 0 (jedná se o nulu na alfanumerické klávesnici) pro přesun do Code Behind a Shift + F7 pro přesun do grafického návrháře.

Code Behind našeho formuláře vypadá asi takto (vynechal jsem jmenné prostory):

public partial class MainWindow : Window
{
        public MainWindow()
        {
                InitializeComponent();
        }
}

Vidíme, že formulář je reprezentován třídou, která dědí z Window. Window je třída, představující prvek Window. Každý ovládací prvek má ve WPF má svou třídu, jak jinak v objektovém jazyce :) V konstruktoru formuláře se volá záhadná metoda InitializeComponents(), která vnitřně naparsuje XAML a vytvoří podle něj instance jednotlivých prvků. Tím se formulář "sestaví".

Pojmenování ovládacích prvků

Když chceme s nějakým prvkem pracovat z Code Behind, musíme mu přiřadit jméno. Starší verze Graphic designeru ovládací prvky nepojmenovávaly vůbec, pozdější je začaly číslovat jako TextBlock1 a podobně. V našem případě budeme při výpočtu potřebovat číst ze dvou elementů TextBox s čísly, elementu ComboBox s operací, psát do TextBlock s výsledkem a obsloužit událost kliknutí na tlačítko.

Přesuneme se zpět do XAMLu a těmto prvkům změníme (ve starších VS přidáme) atribut Name s následující hodnotou pro jednotlivé prvky: cislo1TextBox, cislo2TextBox, operaceComboBox, vysledekTextBlock, vypocitejButton. Všimněte si, že jména jsme zvolili tak, aby končila typem ovládacího prvku. Při větších formulářích je to mnohem přehlednější. Určitě se vyvarujeme názvům jako tlacitko1 a podobně.

Události

WPF jsou postavené na událostech. My budeme v kalkulačce reagovat na jedinou událost, kterou je kliknutí na tlačítko. V designeru na tlačítko poklepeme, budeme přepnuti do Code Behind, kde se nám vygeneruje následující metoda:

private void vypocitejButton_Click(object sender, RoutedEventArgs e)
{
}

Metoda se spustí ve chvíli, kdy na tlačítko uživatel klikne. Jak je toho docíleno se dozvíme později. Události můžeme přiřazovat a mazat také v oknu Properties. Stačí prvek označit a kliknout na ikonu blesku, čímž se přesuneme z vlastností do událostí. Tlačítko vedle nás poté přesune zpět na vlastnosti:

Události ve WPF v C# .NET Visual Studio

Do vygenerované metody vložme následující kód:

// příprava proměnných
string operace = operaceComboBox.Text;
double cislo1 = double.Parse(cislo1TextBox.Text);
double cislo2 = double.Parse(cislo2TextBox.Text);
double vysledek = 0;

// výpočet
if (operace == "+")
        vysledek = cislo1 + cislo2;
else if (operace == "-")
        vysledek = cislo1 - cislo2;
else if (operace == "*")
        vysledek = cislo1 * cislo2;
else if (operace == "/")
{
        if (cislo2 != 0)
                vysledek = cislo1 / cislo2;
        else
                MessageBox.Show("Nulou nelze dělit");
}
vysledekTextBlock.Text = vysledek.ToString();

Na prvních řádcích si připravíme proměnné, do kterých uložíme potřebné hodnoty z ovládacích prvků. K textu v TextBoxu i k vybrané textové položce ComboBoxu se dostaneme přes vlastnost Text. Vidíme zde, proč jsme prvky pojmenovávali. V aplikaci nijak neřešíme situaci, kdy uživatel zadá nesmyslný vstup a program tak spadne na výjimku při parsování této hodnoty. Jak se správně ošetřují chyby si ukážeme až v dalších lekcích, můžete se sem poté vrátit a validaci dodat.

Výpočet výsledku by měl být jasný. Zajímavá je zde pouze kontrola, zda nedělíme nulou. Pokud ano, zobrazíme tzv. MessageBox, což je okno se zprávou pro uživatele, které jistě dobře znáte z jiných aplikací. Slouží nám k tomu stejnojmenná statická třída. MessageBox vypadá asi takto:

MessageBox v C# .NET

Na konci metody již jen přiřadíme do TextBlocku výsledek, který musíme převést na string, jelikož přiřazujeme do vlastnosti Text.

Můžete si aplikaci vyzkoušet.

Kompletní kalkulačka v C# .NET WPF

WPF pod pokličkou

Než začneme zas pokročilejší látku, věnujme několik odstavců tomu, jak aplikace funguje pod pokličkou. Je to trochu taková teorie navíc, pokud jste nedočetli zdejší objektový C# .NET kurz do konce, asi nebudete všemu rozumět, v praktickém vytváření aplikací vám to však nebude nijak bránit.

Parciální třída

Zaměřme se ještě jednou na třídu MainWindow (tedy na Code Behind). Bystřejší z vás si jistě všimli, že třída je parciální (má modifikátor partial). To znamená, že je definována ve více souborech. Ta druhá chybějící část je skrytá a můžeme do ni přejít tak, že klikneme na volání metody InitializeComponent() a stiskneme F12, což nás přenese k její implementaci.

Dostali jsme se do poměrně ošklivé třídy, kterou nám Visual Studio samo vygenerovalo s novým oknem. Vidíme zde dvě metody: InitializeCom­ponent() a Connect().

  • InitializeComponent() si načte XAML a zavolá na něj LoadComponent(). Všimněte si nad třídou atributů ze jmenného prostoru CodeDom. Zde jsou třídy pro generování C# kódu za běhu aplikace. Přesně to metoda dělá, parsuje XAML a vytváří instance ovládacích prvků podle jejich definice v XAMLu.
  • Metoda Connect() zprostředkovává ono magické napojení metod v Code Behind. Vidíme zde, že je použitý EventHandler:
vypocitejButton.Click += new System.Windows.RoutedEventHandler(this.vypocitejButton_Click);

Metoda dále zpřístupňuje jednotlivé ovládací prvky pod jejich názvy, to je realizované tím ošklivým switchem :) Do tohoto souboru nebudeme nikdy nijak zasahovat, je však důležité, abychom chápali, jak WPF funguje.

Vytváření ovládacích prvků za běhu aplikace

Jelikož ovládací prvky jsou obyčejné třídy, určitě vás napadlo, zda je můžeme na formulář přidávat tak, že vytvoříme jejich instance v Code Behind místo toho, abychom je psali do XAMLu. Ano, jde to. Teoreticky bychom XAML kód nemuseli vůbec používat. Prakticky bychom se však v návrhu formulářů vůbec nevyznali.

Ukažme si nějakou část našeho XAMLu, např. Grid. Ten vypadá takto:

<Grid Margin="0">
        <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
</Grid>

V Code Behind bychom stejného výsledku dosáhli tímto zápisem:

Grid grid = new Grid();
grid.Margin = ((Thickness)(TypeDescriptor.GetConverter(typeof(Thickness)).ConvertFromInvariantString("0")));
ColumnDefinition columnDefinition = new ColumnDefinition();
columnDefinition.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("*")));
grid.ColumnDefinitions.Add(columnDefinition);
ColumnDefinition columnDefinition2 = new ColumnDefinition();
columnDefinition2.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("50")));
grid.ColumnDefinitions.Add(columnDefinition2);
ColumnDefinition columnDefinition3 = new ColumnDefinition();
columnDefinition3.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("*")));
grid.ColumnDefinitions.Add(columnDefinition3);
ColumnDefinition columnDefinition4 = new ColumnDefinition();
columnDefinition4.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("50")));
grid.ColumnDefinitions.Add(columnDefinition4);
ColumnDefinition columnDefinition5 = new ColumnDefinition();
columnDefinition5.Width = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("*")));
grid.ColumnDefinitions.Add(columnDefinition5);
RowDefinition rowDefinition = new RowDefinition();
rowDefinition.Height = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("*")));
grid.RowDefinitions.Add(rowDefinition);
RowDefinition rowDefinition2 = new RowDefinition();
rowDefinition2.Height = ((GridLength)(TypeDescriptor.GetConverter(typeof(GridLength)).ConvertFromInvariantString("30")));
grid.RowDefinitions.Add(rowDefinition2);

Asi uznáte, že to není zrovna přehledné a to se jedná o malou část formuláře. Takže právě proto se používá XAML, kde je stromová struktura přehledná a jednoduchá.

Někdy však může být naopak užitečné vytvořit nějakou část aplikace nebo něco donastavit až v Code Behind místo v XAMLu. Účelem této části kurzu bylo, abyste věděli, že to jde.

V příští lekci, Upomínač narozenin v C# .NET WPF - Návrh oken, začneme tvořit robustnější aplikaci, půjde o upomínač narozenin přátel. Zdrojové kódy kalkulačky jsou jako vždy ke stažení níže.


 

Stáhnout

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

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
15 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity (6)

 

 

Komentáře
Zobrazit starší komentáře (22)

Avatar
Michal Štěpánek:8.11.2017 6:18

Myslím, že by bylo lepší (než se vrhneš na kalkulačku) vzít ten seriál o WPF od začátku, ne?

Odpovědět 8.11.2017 6:18
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Martin Šmakala:8.11.2017 15:01

Promiň te ale díval jste se na přiložený obrázek? Kalkulačka v návrháři vypadala vpořádku ale když jsme ji zapl objevilo se to co je na obrázku , kousek 0 chybí.

 
Odpovědět 8.11.2017 15:01
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Martin Šmakala
David Čápka:8.11.2017 15:19

Já nevím, ale na první pohled tedy vidím, že to textové pole je nižší a proto se tam ta nula nevejde :)

Odpovědět 8.11.2017 15:19
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Martin Šmakala:8.11.2017 15:36

díky za váš čas , už asi vím jak to vyřeším , promiňte pokud jsem obtěžoval ,jen mně zajímalo jestli to nebyla chyba třeba VS? Diky

 
Odpovědět 8.11.2017 15:36
Avatar
Odpovídá na Martin Šmakala
Ondřej Štorc:8.11.2017 20:42

No chyba VS to téměř určitě není, ale těžko ti poradíme, když zakryješ tu nejdůležitější čast kódu tím oknem aplikace...

Odpovědět 8.11.2017 20:42
Život je příliš krátký na to, abychom bezpečně odebírali USB z počítače..
Avatar
Karel Labonek:6. března 19:53

Dobry den,
jedna otázečka, jak nastavit oknu ve WPF aby se po spuštění app. zobrazilo přes celou obrazovku děkuji :-)

 
Odpovědět 6. března 19:53
Avatar
Odpovídá na Karel Labonek
Michal Štěpánek:6. března 23:33

Možná to jde i jinak, ale já jsem někde našel toto

InitializeComponent();
this.Height = SystemParameters.PrimaryScreenHeight;
this.Width = SystemParameters.PrimaryScreenWidth;
this.HorizontalAlignment = HorizontalAlignment.Left;
this.VerticalAlignment = VerticalAlignment.Top;
Odpovědět 6. března 23:33
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Odpovídá na Michal Štěpánek
Karel Labonek:7. března 5:39

Děkuji za tvou odpověď,
dnes jsem se probudil a v "properties" si všiml že zde je "WindowState" a při nastavení na "Maximized" to dělá přesně to co potřebuji, při startu app. se hlavní okno zobrazí přes celou obrazovku. No někdy je asi lepší se na to vyspat :-) .
Ale i tak děkuji za odpověď.

 
Odpovědět  +1 7. března 5:39
Avatar
Odpovídá na Karel Labonek
Michal Štěpánek:7. března 6:25

Abych pravdu řekl nepoužívám tento způsob pro celoobrazovkové zobrazení, ale pro zobrazení hlavního okna ve stylu takového "pruhu" s tlačítky a menu, aby to bylo na šířku monitoru s výškou cca 190 a umístěné nahoře...

Odpovědět  +1 7. března 6:25
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Odpovídá na Michal Štěpánek
Karel Labonek:7. března 17:17

Rozumím děkuji :-)

 
Odpovědět 7. března 17:17
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 32. Zobrazit vše