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í.

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

V minulé lekci, Návrh formuláře pro kalkulačku v C# .NET WPF, jsme nakódovali formulář pro jednoduchou kalkulačku.

V dnešním WPF tutoriálu si do naší kalkulačky přidáme jednoduchou logiku. Také si vysvětlíme, jak WPF uvnitř funguje.

Dokončíme naši rozpracovanou kalkulačku z lekce Návrh formuláře pro kalkulačku v C# .NET WPF.

Code Behind

Prezentační část aplikace je napsaná v XAML kódu. 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 F7 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á takto (jmenné prostory jsou vynechány):

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

Vidíme, že formulář je reprezentován třídou, která dědí ze třídy Window. Třída Window představuje prvek Window. Každý ovládací prvek má ve WPF 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í,

a dále:

  • psát do elementu TextBlock s výsledkem a
  • obsloužit událost kliknutí na tlačítko.

Přesuneme se zpět do XAML kódu 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.

Názvy 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 okně 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:

generování události - WPF - Okenní aplikace v C# .NET

Do vygenerované metody vypocitejButton_Click() 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 prvku TextBox i k vybrané textové položce v prvku ComboBox se dostaneme přes vlastnost Text. Zde vidíme, 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.

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é již dobře známe z jiných aplikací. Slouží nám k tomu stejnojmenná statická třída. MessageBox vypadá takto:

message box - WPF - Okenní aplikace v C# .NET

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

Nyní si můžeme aplikaci vyzkoušet:

kalkulačka_spuštění aplikace - WPF - Okenní aplikace v C# .NET

WPF pod pokličkou

Než začneme pokročilejší látku, věnujme několik odstavců tomu, jak aplikace funguje pod pokličkou.

Parciální třída

Zaměřme se ještě jednou na třídu MainWindow (tedy na Code Behind). Všimněme si, ž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á. Do ní přejdeme 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: InitializeComponent() a Connect().

  • InitializeComponent() si načte XAML a zavolá na něj LoadComponent(). Všimněme si atributů nad třídou 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 kódu XAML.
  • 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, což 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, můžeme je na formulář přidávat tak, že vytvoříme jejich instance v Code Behind místo toho, abychom je psali pomocí kódu XAML. 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 kódu XAML, 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);

Vidíme, ž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 kódu XAML.

V následujícím cvičení, Řešené úlohy k 1.-5. lekci WPF v C# .NET, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

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

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

 

Předchozí článek
Návrh formuláře pro kalkulačku v C# .NET WPF
Všechny články v sekci
WPF - Okenní aplikace v C# .NET
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 1.-5. lekci WPF v C# .NET
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
116 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity