Lekce 8 - Upomínač narozenin v C# .NET WPF - Propojení vrstev
V minulé lekci, Upomínač narozenin v C# .NET WPF - Logická vrstva, jsme naprogramovali většinu logické vrstvy naší aplikace Upomínače narozenin.
V dnešním WPF tutoriálu začneme propojovat
logickou a prezentační vrstvu aplikace k
upomínání narozenin. Naučíme se výjimky,
dialogy a bindovat ObservableCollection
na element
ListBox
.
Budeme rozšiřovat naši aplikaci z lekce Upomínač narozenin v C# .NET WPF - Logická vrstva.
Oddělení prezentace a logiky
Nyní máme dokončenou tak zvanou prezentační část aplikace (formuláře) a logickou část (třídy). Tyto dvě vrstvy se v aplikaci striktně oddělují, jelikož jinak by byl kód velmi nepřehledný.
Nikdy bychom neměli provádět výpočty, zápisy do souborů, databáze a podobné věci přímo v kódu formuláře.
Vždy si vytvoříme třídu, která poskytuje příslušné metody a tuto třídu z formuláře pouze používáme. Logika zůstane ve třídě. Třída by naopak vůbec neměla vědět o formuláři. Neměla by tedy např. zobrazovat chybové hlášky, ale pouze vyvolávat v případě chyby výjimky. Je potom na formuláři, aby uživateli chybu zobrazil. Právě formulář je ta část aplikace, která s uživatelem komunikuje. Žádná jiná to nedělá.
Naše jednoduchá kalkulačka, kterou jsme vytvořili v prvních lekcích kurzu, byla návrhově špatně. Z důvodu jednoduchosti jsme napsali výpočty rovnou do obslužné metody tlačítka. Správně bychom měli mít nějakou třídu, která by výsledky počítala a její metody bychom z formuláře pouze volali. XAML a i jeho Code Behind je tedy stále prezentační vrstva aplikace.
XAML definuje jak formulář vypadá, Code Behind volá logiku, kterou neobsahuje.
Dnes si tedy ukážeme, jak se to dělá správně.
Propojení prezentace a logiky v naší aplikaci
Přejdeme do Code Behind formuláře MainWindow
a třídě
přidáme privátní atribut typu SpravceOsob
, ve kterém rovnou
vytvoříme instanci správce:
private readonly SpravceOsob spravceOsob = new();
Instance správce se vytvoří při vytvoření formuláře a formulář s ní dále bude komunikovat a tak provádět úkony, které si přeje uživatel.
Přidání osob
Abychom něco konečně také viděli, přejdeme k přidání osob.
Kód formuláře
OsobaWindow
Nejprve přejdeme do kódu formuláře OsobaWindow
, kde si
stejně jako v předchozím formuláři připravíme správce osob jako
privátní atribut. Kdybychom zde i vytvořili jeho instanci, nebylo by
nám to moc platné, jelikož osoby by byly načtené ve správci v hlavním
formuláři. A načítat je znovu by bylo neefektivní. Necháme si tedy
hotového a načteného správce předat konstruktorem a uložíme ho do
připravené proměnné:
private readonly SpravceOsob spravceOsob; public OsobaWindow(SpravceOsob spravceOsob) { InitializeComponent(); this.spravceOsob = spravceOsob; }
Nyní naklikneme tlačítko OK a do správce přidáme osobu pomocí hodnot,
které uživatel do jednotlivých prvků zadal. K hodnotě v elementu
DatePicker
přistoupíme pomocí vlastnosti
SelectedDate
, která je nullovatelného typu
DateTime?
.
Metoda OkButton_Click()
V lekci Upomínač
narozenin v C# .NET WPF - Logická vrstva jsme zadali, že metoda
Pridej()
vyvolá výjimku v případě příliš krátkého jména,
nevyplněného data nebo data v budoucnosti. Proto kód vložíme do bloku
try
, za kterým bude následovat blok catch
. Pokud
výjimka v bloku try
nastane, program se ihned přesune do bloku
catch
, kde pomocí okna MessageBox
zobrazí chybovou
hlášku:
private void OkButton_Click(object sender, RoutedEventArgs e) { try { spravceOsob.Pridej(jmenoTextBox.Text, narozeninyDatePicker.SelectedDate); Close(); } catch (Exception ex) { MessageBox.Show(ex.Message, "Chyba", MessageBoxButton.OK, MessageBoxImage.Exclamation); } }
Pokud bychom výjimku neošetřili blokem
try-catch
, způsobila by pád aplikace.
Ke zprávě výjimky se dostaneme přes vlastnost Message
.
Zprávě nastavujeme ikonu vykřičníku. V metodě již neřešíme žádnou
logiku a je díky tomu poměrně krátká.
V tomto formuláři jsme skončili.
Kód formuláře MainWindow
Přesuneme se zpět do třídy MainWindow
, tentokrát do
grafického návrháře. Naklikneme tlačítko pridatButton
a jeho
obslužnou metodu upravíme takto:
private void PridatButton_Click(object sender, RoutedEventArgs e) { OsobaWindow osobaWindow = new(spravceOsob); osobaWindow.ShowDialog(); }
V obslužné metodě tlačítka pridatButton
vytvoříme novou
instanci formuláře OsobaWindow
, které předáme místního
správce osob. Na instanci následně zavoláme metodu
ShowDialog()
. Ta zobrazí nový formulář (stejně jako metoda
Show()
) a k tomu ještě zablokuje zbytek aplikace, který se
zaktivní až v případě, kdy se dialog zavře. Výsledkem je, že se s
hlavním formulářem nedá pracovat dokud dialog nepotvrdíme či neukončíme.
U dialogů se toto většinou dělá (dialogy rozumějme pomocné formuláře,
který slouží nejčastěji k zadání dat). I když nám by v podstatě
nevadilo, kdyby uživatel během zadávání nové osoby aplikaci používal a
otevřel třeba další dialog k zadávání.
Když bychom aplikaci nyní spustili, osobu bychom sice přidali do kolekce
ObservableCollection
, nicméně bychom ji v elementu
ListBox
neviděli. Je to samozřejmě z toho důvodu, že
ListBox
není na tuto kolekci napojený. Toho docílíme pomocí
bindingu.
Při naklikávání tlačítek se metoda Click()
vygeneruje v souladu s názvem tlačítka. To znamená, že metoda bude
začínat malým písmenem. Opravu je nejlépe nechat provést Visual Studio
pomocí Quick Actions.
Binding
Pokud chceme bindovat, musíme oknu nejprve nastavit tak zvaný kontext. To
je objekt, jehož vlastnosti budeme bindovat. To uděláme nejjednodušeji v
konstruktoru třídy MainWindow
:
DataContext = spravceOsob;
Instance správce osob je nyní nastavena jako datový kontext hlavního okna a my v tomto okně můžeme zobrazovat její vlastnosti.
Přesuneme se do XAML a elementu <ListBox>
dodáme atribut
ItemsSource
:
<ListBox Name="osobyListBox" Grid.Column="0" Grid.Row="2" Margin="0, 0, 0, 10" ItemsSource="{Binding Osoby}"/>
Tím říkáme, že zdrojem položek v elementu ListBox
je
vlastnost Osoby
na kontextu, kterým je správce osob.
Testování
Nyní si aplikaci spustíme a přidáme nějaké osoby:

Přidané osoby se ihned objeví v elementu ListBox
díky
bindingům. ListBox
vždy zobrazuje to, co vrací metoda
ToString()
jeho položek. U osob tedy zobrazuje jejich jméno.
Podobně fungují samozřejmě i další ovládací prvky, např.
ComboBox
.
Odebrání osob
Obslužná metoda tlačítka odebratButton
v kódu formuláře
MainWindow
bude vypadat takto:
private void OdebratButton_Click(object sender, RoutedEventArgs e) { if (osobyListBox.SelectedItem != null) { spravceOsob.Odeber((Osoba)osobyListBox.SelectedItem); } }
Důležitá je podmínka, která zjišťuje, zda je v elementu
ListBox
vybraná nějaká položka. K vybrané položce se
dostaneme přes vlastnost SelectedItem
. Položku následně
přetypujeme na Osoba
, jelikož je typu object
(to aby
byl ListBox
univerzální). Tuto osobu předáme metodě
Odeber()
na správci, která dále vykoná fyzické odebrání z
ObservableCollection
.
V příští lekci, Upomínač narozenin v C# .NET WPF - Bindingy, se seznámíme se základy bindingů v C# .NET WPF včetně jejich módů. Bindingy implementujeme do našeho Upomínače narozenin.
Měl jsi s čímkoli problém? Zdrojový kód vzorové aplikace je ke stažení každých pár lekcí. Zatím pokračuj dál, a pak si svou aplikaci porovnej se vzorem a snadno oprav.