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

C# .NET WPF Code Behind v C# .NET WPF a dokončení kalkulačky American English version English version

V minulém dílu našeho seriálu tutoriálů o tvorbě okenních aplikací v C# .NET WPF jsme nakódovali formulář pro jednoduchou kalkulačku. Dnes 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.

Příště 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 431x (143.94 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (3)

Článek pro vás napsal David Čápka
Avatar
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.

Jak se ti líbí článek?
Celkem (10 hlasů) :
55555


 



 

 

Komentáře

Avatar
Jan Vargovský
Redaktor
Avatar
Jan Vargovský:

David Čápka schválně jsem zkoušel to CTRL+ALT+0, protože jsem zvyklý na F7 a z5 pak jak už píšeš SHIFT+F7, ale ta první zkratka mi nejde :)

Ale jinak pěkné :)

 
Odpovědět 11.12.2013 13:56
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Jan Vargovský
David Čápka:

Jde od VS 2012, možná máš staré. F7 už nefunguje.

Odpovědět 11.12.2013 14:26
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
Jan Vargovský
Redaktor
Avatar
Odpovídá na David Čápka
Jan Vargovský:

Mám VS 2012 s nejnovějším updatem. To spíše ty máš old verzi, protože když sem to u sebe otvíral, tak to převádělo do novější verze VS :)

 
Odpovědět 11.12.2013 15:35
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovědět 11.12.2013 15:40
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
Jan Vargovský
Redaktor
Avatar
Odpovídá na David Čápka
Jan Vargovský:

=> tak to je od 2013 :)

EDIT: Co tam máš teda na F7 ?

Editováno 11.12.2013 19:52
 
Odpovědět 11.12.2013 19:50
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Jan Vargovský
David Čápka:

F7 nedělá nic. Bylo to tak stoprocentně i v 2012, měl jsem ho já i lidi co jsem učil. Ale věřím ti, že ty to máš jinak, třeba to přebralo nastavení z VS 2010 pokud jsi to upgradoval.

Odpovědět 11.12.2013 20: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
coells
Redaktor
Avatar
Odpovídá na David Čápka
coells:

Pánové, nenechte se zmást - funguje F7 i shift-F7, ale záleží na tom, jaké máte nastavené prostředí. Při instalaci studia se to ptá, jaké chcete prostředí - C#, VB, Web developer, atd. Nová instalace studia umí přebírat nastavení ze starší verze. A pokud máte každý jiné prostředí, nikdy se na zkratkách neshodnete.

 
Odpovědět  +1 11.12.2013 20:06
Avatar

Člen
Avatar
:

Článek dobrý. Chválím. Osobně se domnívám, že nejrychlejší způsob jak se dostat do Code Behind, je jednoduše kliknout na MainWindow.xaml.cs. Avšak samozřejmě je dobré znát všechny alternativy.

 
Odpovědět  +2 15.7.2014 15:52
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 8 zpráv z 8.