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:
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:
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:
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ějLoadComponent()
. Všimněme si atributů nad třídou ze jmenného prostoruCodeDom
. 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 835x (400.42 kB)
Aplikace je včetně zdrojových kódů v jazyce C#