Lekce 7 - Upomínač narozenin v C# .NET WPF - Logická vrstva
V minulé lekci, Upomínač narozenin v C# .NET WPF - Návrh oken, jsme začali pracovat na aplikaci k upomínání
narozenin v C# .NET WPF. Představili jsme si elementy
<StackPanel>, <ListBox>,
<Image>, <Calendar> a
<Label>.
V dnešním WPF tutoriálu se budeme zabývat návrhem logické vrstvy.
Logická vrstva obsahuje třídy s logikou aplikace.
Budeme rozšiřovat naši aplikaci z lekce Upomínač narozenin v C# .NET WPF - Návrh oken.
Třída Osoba
V naší aplikaci budou zcela jistě figurovat osoby. Vytvořme jim tedy
třídu. Před třídu Osoba si dáme modifikátor
public.
Vlastnosti
Osoba bude mít 4 vlastnosti:
Jmeno– jméno typustring.Narozeniny– narozeniny typuDateTime.Vek– věk typuint.ZbyvaDni– zbývající počet dní do nejbližších narozenin typuint.
První dvě vlastnosti jsou jednoduché:
public string Jmeno { get; set; } public DateTime Narozeniny { get; set; }
Tyto vlastnosti nastavíme pomocí parametrického konstruktoru. Třída vypadá zatím takto:
public class Osoba { public string Jmeno { get; set; } public DateTime Narozeniny { get; set; } public Osoba(string jmeno, DateTime narozeniny) { Jmeno = jmeno; Narozeniny = narozeniny; } }
Další dvě vlastnosti nebudou pouhou proměnnou, ale budou obsahovat další logiku. Takové vlastnosti je potom možné pomocí bindingu jednoduše zobrazovat na formuláři.
Vlastnost Vek
Vlastnost vypočítá a vrátí aktuální věk osoby v celých letech.
Výpočet bohužel není jen o tom odečíst dva datumy, jelikož
TimeSpan neumí zjistit počet let, pouze počet dní. Výpočet
věku tedy provedeme následujícím způsobem:
- Získáme aktuální datum (bez času) z vlastnosti
Todayna struktuřeDateTime. - Věk spočítáme jako rozdíl roků v aktuálním datu a datu narozenin. Je jasné, že tento věk není přesný. Pokud jsme se narodili 1.2.1990 a je 1.1.2010, není nám 20 let, ale jen 19. Z toho důvodu provedeme korekci.
- V případě, že je aktuální datum menší (dříve) než datum narození po přidání námi vypočítaných let, nastal výše uvedený případ a my věk o rok snížíme.
- Hotový věk vrátíme.
Kód vlastnosti je následující:
public int Vek { get { DateTime dnes = DateTime.Today; int vek = dnes.Year - Narozeniny.Year; if (dnes < Narozeniny.AddYears(vek)) vek--; return vek; } }
Vlastnost ZbyvaDni
Vlastnost nám vrátí kolik dní zbývá do narozenin osoby. To zjistíme následujícím způsobem:
- Získáme datum dalších narozenin přičtením věku
+ 1k datu narození. - Data odečteme a celkový rozdíl ve dnech vrátíme.
Jelikož je rozdíl typu
double, musíme ho zkonvertovat naint.
Kód vlastnosti je následující:
public int ZbyvaDni { get { DateTime dalsiNarozeniny = Narozeniny.AddYears(Vek + 1); TimeSpan rozdil = dalsiNarozeniny - DateTime.Today; return Convert.ToInt32(rozdil.TotalDays); } }
Metoda ToString()
Jelikož budeme osoby vypisovat, přepíšeme třídě ještě metodu
ToString() tak, aby vracela jméno osoby:
public override string ToString() { return Jmeno; }
Třída SpravceOsob
Další logickou komponentou aplikace bude správce osob. Třída se bude starat o osoby. Bude je umět přidávat, odebírat a jejich seznam ukládat do souboru a opětovně načíst. Konečně bude umět mezi osobami vyhledat tu, která má nejbližší narozeniny.
Přidejme si k projektu tedy třídu SpravceOsob a učiňme ji
veřejnou.
Vlastnosti
Správce osob bude disponovat třemi veřejnými vlastnostmi.
Vlastnost Osoby
První je seznam osob typu
ObservableCollection. S touto kolekcí jsme se v kurzu ještě
nesetkali. Jedná se o chytřejší List, který na sobě umí
vyvolat událost změny v případě, že se změní jeho obsah. Díky tomuto
mechanismu se automaticky obnoví všechny prvky na formuláři, které mají
nastavený jako zdroj dat právě tuto kolekci
ObservableCollection. Asi si dokážeme představit, že obnovovat
ručně desítky formulářových prvků v případě změny může být velice
nepřehledné. Jakmile v naší aplikaci přidáme novou osobu, bude ihned
vidět v seznamu osob aniž bychom ho obnovovali, obnoví se sám.
ObservableCollection() inicializujeme v konstruktoru.
Pokud bychom chtěli implementovat i editaci osob, musela by
třída Osoba implementovat rozhraní
INotifyPropertyChanged. Jakákoli změna (např. jména) by se
poté automaticky projevila ve všech prvcích všech formulářů, kde osoba
figuruje. Tímto se však pro zjednodušení zabývat nebudeme.
Třída SpravceOsob zatím vypadá takto:
public class SpravceOsob { public ObservableCollection<Osoba> Osoby { get; set; } public SpravceOsob() { Osoby = []; } }
Vlastnosti
DnesniDatum a NejblizsiOsoba
Dalšími vlastnostmi jsou:
DnesniDatum, která vrátí dnešní datum.NejblizsiOsoba, která vrátí osobu s nejbližšími narozeninami.
Jejich kód je:
public Osoba? NejblizsiOsoba { get; set; } public DateTime DnesniDatum { get { return DateTime.Now; } }
Metody
Kromě přidávání a odebírání bude třída umět i nalézt osobu s nejbližšími narozeninami. Ukládání a načítání osob do/ze souboru necháme na později.
Metoda NajdiNejblizsi()
V privátní metodě NajdiNejblizsi() najdeme a uložíme osobu,
která má nejbližší narozeniny. K vyhledání osoby v seznamu použijeme LINQ metodu OrderBy(), která
osoby uspořádá podle toho, kolik dní zbývá do jejich narozenin. Výsledek
uložíme do kolekce, jejíž typ neuvedeme a použijeme místo toho klíčové
slovo var, jak je v LINQ zvykem. Následně vrátíme první
osobu:
private void NajdiNejblizsi() { var serazeneOsoby = Osoby.OrderBy(o => o.ZbyvaDni); if (serazeneOsoby.Any()) NejblizsiOsoba = serazeneOsoby.First(); else NejblizsiOsoba = null; }
LINQ metodu First() bychom měli volat jen tehdy,
když jsou v seznamu nějaké osoby. Proto se na to ptáme LINQ metodou
Any().
Metoda Pridej()
V metodě Pridej() přidáme novou osobu do kolekce
ObservableCollection. Jelikož osobu budeme přidávat
formulářem, hodí se nám, aby metoda brala v parametrech vlastnosti osoby a
na jejich základě vytvořila novou instanci. Z data narození uložíme pouze
část s datem, tedy bez času.
Před přidáním zkontrolujeme, zda jméno není příliš
krátké nebo není zadané datum v budoucnosti. Pokud
nějaká z těchto situací nastane, vyvoláme výjimku. V
našem případě se nám hodí výjimka ArgumentException (chyba v
argumentu). Do konstruktoru výjimky zadáváme text chyby. Jakmile je výjimka
vyvolána, metoda již dále nepokračuje. Jak na chybu reagovat si ukážeme
až budeme metodu volat z formuláře.
Výjimky jsou jediným správným způsobem, jak pracovat s chybami v objektových aplikacích.
Při zadávání data ovládacím prvkem DatePicker dostaneme
datum jako typ DateTime?, který může nabývat hodnoty
null. Pokud je v datu null, znamená to, že nebylo
zadané a vyhodíme též výjimku. K hodnotě nullovatelného typu
přistupujeme přes vlastnost Value. Na konci metody obnovíme
nejbližší osobu, protože to může být zrovna ta přidaná:
public void Pridej(string jmeno, DateTime? datumNarozeni) { if (jmeno.Length < 3) throw new ArgumentException("Jméno je příliš krátké"); if (datumNarozeni == null) throw new ArgumentException("Nebylo zadané datum narození"); if (datumNarozeni.Value.Date > DateTime.Today) throw new ArgumentException("Datum narození nesmí být v budoucnosti"); Osoba osoba = new Osoba(jmeno, datumNarozeni.Value.Date); Osoby.Add(osoba); NajdiNejblizsi(); }
Metoda Odeber()
V metodě odebereme osobu z kolekce ObservableCollection.
Protože vždy budeme chtít odebírat již hotovou osobu, vezmeme v parametru
právě tu. Po odebrání opět obnovíme nejbližší osobu:
public void Odeber(Osoba osoba) { Osoby.Remove(osoba); NajdiNejblizsi(); }
V příští lekci, Upomínač narozenin v C# .NET WPF - Propojení vrstev, 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.
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.

David se informační technologie naučil na