Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

7. díl - Upomínač narozenin v C# .NET WPF - Logická vrstva

C# .NET Formuláře WPF Upomínač narozenin v C# .NET WPF - Logická vrstva

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Upomínač narozenin v C# .NET WPF - Návrh oken, jsme navrhli kompletně formuláře pro naši aplikaci. V tomto tutoriálu se budeme zabývat návrhem logické vrstvy, tedy tříd, které obsahují logiku aplikace.

Osoba

V naší aplikaci budou zcela jistě figurovat osoby, vytvořme jim tedy třídu. Před třídu nezapomeňte dát modifikátor public.

Vlastnosti

Osoba bude mít 4 vlastnosti:

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

Věk

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 si aktuální datum (bez času) pomocí DateTime.Today.
  2. Věk spočítáme jako rozdíl roků v aktuálním datu a datu narozenin. Asi je vám 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 bude 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;
        }
}
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.
public int ZbyvaDni
{
        get
        {
                DateTime dnes = DateTime.Today;
                DateTime dalsiNarozeniny = Narozeniny.AddYears(Vek + 1);

                TimeSpan rozdil = dalsiNarozeniny - DateTime.Today;

                return Convert.ToInt32(rozdil.TotalDays);
        }
}

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;
}

Správce osob

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řidejte si k projektu tedy třídu SpravceOsob a učiňte ji veřejnou.

Vlastnosti a atributy

Správce osob bude disponovat třemi veřejnými vlastnostmi.

První je seznam osob typu ObservableCollec­tion. S touto kolekcí jsme se v seriálu 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 kontrolky na formuláři, které mají nastavený jako zdroj dat právě tuto ObservableCollec­tion. Asi si dokážete představit, že obnovovat ručně desítky kontrolek na formuláři 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. ObservableCollec­tion() inicializujeme v konstruktoru.

Pokud bychom chtěli implementovat i editaci osob, musela by třída Osoba implementovat rozhraní INotifyProper­tyChanged. Jakákoli změna (např. jména) by se poté automaticky projevila ve všech kontrolkách všech formulářů, kde osoba figuruje. Tímto se však pro zjednodušení zabývat nebudeme.

Třída zatím vypadá takto:

public class SpravceOsob
{
        public ObservableCollection<Osoba> Osoby { get; set; }

        public SpravceOsob()
        {
                Osoby = new ObservableCollection<Osoba>();
        }

}

Dalšími vlastnostmi jsou:

  • DnesniDatum, která vrátí dnešní datum.
  • Dále NejblizsiOsoba, která vrátí osobu s nejbližšími narozeninami.

Jejich kód:

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.

NajdiNejblizsi()

Metoda najde a uloží 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 u LINQu zvykem. Následně vrátíme první osobu. Metodu bychom měli volat jen tehdy, když jsou v seznamu nějaké osoby. Bude také privátní, jelikož ji budeme volat jen uvnitř třídy. Ačkoli by z kódu mělo být jasné, co metoda dělá, můžete se samozřejmě podívat na tutoriály v sekci Kolekce a LINQ v C# .NET, kde je technologie LINQ detailně popsána.

private void NajdiNejblizsi()
{
        var serazeneOsoby = Osoby.OrderBy(o => o.ZbyvaDni);
        if (serazeneOsoby.Count() > 0)
                NejblizsiOsoba = serazeneOsoby.First();
        else
                NejblizsiOsoba = null;
}
Pridej()

Metoda přidá novou osobu do ObservableCollec­tion. 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, vyhodíme výjimku. Právě výjimky jsou jediným správným způsobem, jak s chybami v objektových aplikacích pracovat.

Výjimky jsou detailněji popsané v sekci Práce se soubory v C# .NET. Pokud jste se s nimi ještě nesetkali, bude vám stačit vědět, že výjimka se vyvolá pomocí klíčového slova throw, za kterým následuje instance výjimky. Výjimek máme několik typů včetně možnosti vytvořit si vlastní. V našem případě se nám hodí 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.

Při zadávání data kontrolkou DatePicker dostaneme datum jako typ DateTime?. Pokud jste dočetli objektový seriál až do konce, víte, že otazník značí tzv. nullovatelný typ. To je jakési rozšíření hodnotového datového typu o hodnotu null, kterou hodnotový typ běžně obsahovat nemůže. Pokud je v datu null, znamená to, že nebylo zadané a vyhodíme též výjimku. K hodnotě nullovatelného typu přistupuje přes vlastnost Value.

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();
}

Na konci metody obnovíme nejbližší osobu, protože to může být zrovna ta přidaná.

Odeber()

Metoda odebere osobu z ObservableCollec­tion. Protože vždy budeme chtít odebírat již hotovou osobu, bere metoda v parametru právě tu. Po odebrání opět obnovíme nejbližší osobu.

public void Odeber(Osoba osoba)
{
        Osoby.Remove(osoba);
        NajdiNejblizsi();
}

Pokračovat budeme v lekci, Upomínač narozenin v C# .NET WPF - Propojení vrstev, kdy aplikaci zprovozníme. Dosavadní zdrojový kód je ke stažení níže.


 

Stáhnout

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

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
16 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity (4)

 

 

Komentáře
Zobrazit starší komentáře (11)

Avatar
Honza.G
Člen
Avatar
Honza.G:13.11.2016 15:29

Čau,
menší otázka. Učím se s tímto poprvé v životě a docela mě to všechno mate. Proč se to píše tak a né jinak k čemu to tam konrkétně slouží, jak se to dá použít jinak a tak. Mate to jen mě(třeba na to nemám buňky), nebo to chce čas abych to poznal a zvykl si?

 
Odpovědět 13.11.2016 15:29
Avatar
Odpovídá na Honza.G
Ondřej Štorc:13.11.2016 16:02

Jestli ti to není jasný a nečtli jsi předchozí články (jak základy programování, tak OOP), tak bych ti doporučil si nejdříve přečíst je. Všechno to vyplývá z nich.

Odpovědět  +1 13.11.2016 16:02
Život je příliš krátký na to, abychom bezpečně odebírali USB z počítače..
Avatar
Odpovídá na Honza.G
Libor Šimo (libcosenior):13.11.2016 16:13

Potrebne zaklady najdes tu a ostatne najdes na nete.
Najlepsie bude, ked si vymyslis vlastny projekt a pomaly ho naprogramujes.
Ja som si napisal evidenciu materialu (vydaj, prijem, pohyb atd.) pre firmu kde pracujem a teraz pisem pre cukrarku program na evidenciu zakuskov (druhy, rozpis surovin, nakup surovin, objednavka atd.).
Fantazii sa medze nekladu a vsetko sa da krasne napisat a doladit.

Odpovědět  +1 13.11.2016 16:13
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Martin Michale:14.12.2016 11:33

Zdravim, autor v clanku zminuje v pripade pouziti editace implementovat rozhraní INotifyProper­tyChanged.
Dalo by se nekde videt tento priklad s vyuzitim editace i s timto rozhranim ?

 
Odpovědět 14.12.2016 11:33
Avatar
Viktor Abel
Člen
Avatar
Viktor Abel:15.12.2016 23:02

Super! Děkuji za typ-takto už to funguje..

 
Odpovědět 15.12.2016 23:02
Avatar
Honza Rada
Člen
Avatar
Honza Rada:15. června 21:28

Ve observateColection není addRange?

Odpovědět 15. června 21:28
#c#
Avatar
Marek
Člen
Avatar
Marek:13. září 23:33

Ahoj,

omlouvám se za hloupou otázku. Proč nyní musíme psát před každou třídu modifikátor public ? U Arény jsme také měli v jednom projektu více tříd a stačilo mít stejný jmenný prostor a nebylo třeba mít třídu označenou jako public.

Díky.

 
Odpovědět 13. září 23:33
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Marek
David Oczka:14. září 8:21

Nevím, jak se to implementovalo v Aréně, ale pokud chceš použít nějakou třídu v jiné, musí být public.
To, že jsou ve stejném jmenném prostoru znamená jen to, že je můžeš volat, aniž bys musel přidávat v ostatních souborech (se stejným jmenným prostorem) další referenci (using nejaky_jmenny_pros­tor;).

 
Odpovědět  +1 14. září 8:21
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Marek
David Čápka:14. září 10:09

Je to kvůli tomu, když v té třídě používáš vlastnosti. Public vlastnost v ne-public třídě dělá u některých datových typů problémy, mám pocit, že u struktur, což je třeba právě DateTime. Vyhodí to nějakou hlášku, že třída je méně viditelná než ta vlastnost.

Odpovědět  +1 14. září 10:09
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Marek
Člen
Avatar
Odpovídá na David Oczka
Marek:14. září 21:02

Díky za odpověď. Kontroloval jsem tu Arénu a opravdu tam jsou všechny třídy internal, tak tam asi bude nějaká spojitost s vlastnostmi, jak píše David, jelikož atributy v Aréně fungovaly dobře..

 
Odpovědět 14. září 21:02
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 21. Zobrazit vše