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 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 typu string.
  • Narozeniny – narozeniny typu DateTime.
  • Vek – věk typu int.
  • ZbyvaDni – zbývající počet dní do nejbližších narozenin typu int.

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:

  1. Získáme aktuální datum (bez času) z vlastnosti Today na struktuře DateTime.
  2. 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.
  3. 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.
  4. 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:

  1. Získáme si aktuální datum (bez času).
  2. Získáme datum dalších narozenin přičtením věku + 1 k datu narození.
  3. Data odečteme a celkový rozdíl ve dnech vrátíme. Jelikož je rozdíl typu double, musíme ho zkonvertovat na int.

Kód vlastnosti je následující:

public int ZbyvaDni
{
    get
    {
        _ = DateTime.Today;
        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 OrderBy() 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.

Předchozí článek
Upomínač narozenin v C# .NET WPF - Návrh oken
Všechny články v sekci
WPF - Okenní aplikace v C# .NET
Přeskočit článek
(nedoporučujeme)
Upomínač narozenin v C# .NET WPF - Propojení vrstev
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
81 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