5. díl - 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í kontrolku Window. Každá kontrolka ve WPF má svou třídu, jak jinak v objektovém jazyce :) V konstruktoru formuláře se volá záhadná metoda InitializeCom­ponents(), která vnitřně naparsuje XAML a vytvoří podle něj instance jednotlivých kontrolek. Tím se formulář "sestaví".

Pojmenování kontrolek

Když chceme s nějakou kontrolkou pracovat z Code Behind, musíme ji přiřadit jméno. Starší verze Graphic designeru kontrolky 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 z dvou TextBoxů s čísly, ComboBoxu s operací, psát do TextBlocku s výsledkem a obsloužit událost kliknutí na tlačítko.

Přesuneme se zpět do XAMLu a těmto kontrolkám změníme (ve starších VS přidáme) atribut Name s následující hodnotou pro jednotlivé kontrolky: cislo1TextBox, cislo2TextBox, operaceComboBox, vysledekTextBlock, vypocitejButton. Všimněte si, že jména jsme zvolili tak, aby končila typem kontrolky. 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čí kontrolku 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 kontrolek. K textu v TextBoxu i k vybrané textové položce ComboBoxu se dostaneme přes vlastnost Text. Vidíme zde, proč jsme kontrolky 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 dílech, 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ý seriál 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 InitializeCom­ponent() 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().

InitializeCom­ponent() 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 kontrolek podle jejich definice v XAMLu.

Metoda Connect() zprostředkovává ono magické napojení metod v CodeBehind. Vidíme zde, že je použitý EventHandler:

vypocitejButton.Click += new System.Windows.RoutedEventHandler(this.vypocitejButton_Click);

Metoda dále zpřístupňuje jednotlivé kontrolky 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í kontrolek za běhu aplikace

Jelikož kontrolky 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 seriálu 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 498x (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 se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity (4)

 

 

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

Avatar
Michal Štěpánek:12. června 21:04

tu metodu musíš v xamlu přiřadit tomu tlačítku Když do řádku s tím buttonem připíšeš click="

<Button Name="btn" Content="Tlačítko" Click="...

Napoví ti intelisense ve VS něco jako Přidat novou metodu a samo si ji pojmenuje a vytvoří...

Odpovědět 12. června 21:04
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Filip Pawera
Člen
Avatar
Odpovídá na Michal Štěpánek
Filip Pawera:12. června 21:23

Díky za tip. Zkusil jsem a vyhodilo mi to novou hlášku. Napadlo mě změnit "x:Name=vypoci­tejButton" na "Name=vypocitej­Button" a také nic. Budu ještě pokračovat, jestliže budete mít tipy, tak jsem jen rád. Jak by se mi to nějak povedlo, tak napíšu. Hláška je v příloze.

 
Odpovědět 12. června 21:23
Avatar
Odpovídá na Filip Pawera
Michal Štěpánek:12. června 21:27

Asi bych zkusil dát VS do továrního nastavení, bo sis asi něco někde přenastavil...

Odpovědět 12. června 21:27
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Filip Pawera
Člen
Avatar
Odpovídá na Michal Štěpánek
Filip Pawera:13. června 10:16

Zdravím,
tovární nastavení nepomohlo. Každopádně díky. Pozapínal jsem více projektů, stáhl jsem i zdejší kalkulačku a zkoušel, také jsem založil nový WPF projekt a normálně mi vytváření metody u tlačítka jede všemi způsoby. Jen tento jediný projekt, co jsem tady vložil jeho kód, nefunguje. :-? Samozřejmě bych se na to mohl vykašlat a začít znovu, ale postupoval jsem dle návodu zde a rád bych se dopátral, kde je chyba, protože teď jde sice jen o lekci, ale když by se mi to mělo stát u delšího kódu, tak ať vím, co s tím dělat :-)

 
Odpovědět 13. června 10:16
Avatar
Odpovídá na Filip Pawera
Michal Štěpánek:13. června 10:27

Zkus si otevřít svůj projekt i ten stažený a porovnej je...

Odpovědět  +1 13. června 10:27
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Filip Pawera
Člen
Avatar
Filip Pawera:13. června 10:32

Řešení jsem našel. Kód jsme zkopíroval do nového projektu a chybu to házelo. Takže jsme procházel řádek po řádku, znak po znaku a objevil jsem, že hned na začátku v <Window x:Class="Kalku­lacka.MainWin­dow " jsem měl za w mezeru. Opravil jsem to na <Window x:Class="Kalku­lacka.MainWin­dow" a jede to :-)

 
Odpovědět 13. června 10:32
Avatar
Filip Pawera
Člen
Avatar
Filip Pawera:13. června 10:33

Ještě jednou díky za tipy a rady :-)

 
Odpovědět 13. června 10:33
Avatar
Luboš
Člen
Avatar
Luboš:16. července 18:51

Ahoj, David zde zřejmě popisuje pouze jeden způsob jak programovat ve WPF. Našel jsem další způsoby pomocí XAMLu zde: XAML - Data Binding XAML - Markup Extensions XAML - Dependency Properties
Nevíte někdo ze zkušenosti co je lepší? Je něco co nejde naprogramovat jen pomocí Code Behind?

 
Odpovědět 16. července 18:51
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Luboš
David Čápka:16. července 19:02

Mícháš moc věcí dohromady. V prvním odkazu jsou bindingy, ty jsou zde probrané. Druhý odkaz je o definování vlastní syntaxe v XAML. Třetí je o Dependency properties, které umí navíc nějaké věci oproti standardním vlastnostem. Nevidím nikde ale nějaký jiný způsob, jak ve WPF programovat. Všechno je to XAML a Code behind.

Editováno 16. července 19:03
Odpovědět 16. července 19:02
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Luboš
Člen
Avatar
Odpovídá na David Čápka
Luboš:16. července 22:30

Díky za reakci. Pomalu to začínám chápat.

 
Odpovědět 16. července 22:30
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 20. Zobrazit vše