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

C# .NET WPF Upomínač narozenin v C# .NET WPF - Logická vrstva American English version English version

V minulém dílu našeho seriálu tutoriálů o tvorbě okenních aplikací v C# .NET WPF jsme navrhli kompletně formuláře pro naši aplikaci. V tomto dí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 příště, kdy aplikaci zprovozníme. Dosavadní zdrojový kód je ke stažení níže.


 

Stáhnout

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

 

  Aktivity (3)

Článek pro vás napsal David Čápka
Avatar
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.

Jak se ti líbí článek?
Celkem (11 hlasů) :
4.363644.363644.363644.36364 4.36364


 



 

 

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

Avatar
Filistin
Člen
Avatar
Filistin:

mám na mysli takový ten zápis

From j in kolekce
where podminka
select neco
 
Odpovědět 25.11.2015 19:55
Avatar
krepsy3
Redaktor
Avatar
krepsy3:

Chci se zeptat; u tutoriálu k WF je u SpravceOsob použitá kolekce BindingList<>, jaký je rozdíl mezi BindingListem a ObservableCollec­tion<>?

Odpovědět 4. dubna 13:23
Programátor je stroj k převodu kávy na kód.
Avatar
Odpovídá na krepsy3
Michal Štěpánek:

Výcuc z článku nahoře:

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.

Odpovědět 4. dubna 14:00
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
krepsy3
Redaktor
Avatar
Odpovídá na Michal Štěpánek
krepsy3:

Ano chápu. Přikládám výcuc z článku týkající se stejné problematiky u WF:

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ě tento BindingList.

A mě zajímal právě rozdíl mezi ObservableCollec­tion<> a BindingList<>

Odpovědět 4. dubna 20:03
Programátor je stroj k převodu kávy na kód.
Avatar
Odpovídá na krepsy3
Michal Štěpánek:

V podstatě BindingList je pro WF a ObservableCollec­tion pro WPF...
http://stackoverflow.com/…-bindinglist

Editováno 4. dubna 23:11
Odpovědět 4. dubna 23:09
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
krepsy3
Redaktor
Avatar
Odpovědět 5. dubna 7:04
Programátor je stroj k převodu kávy na kód.
Avatar
prokop.mejstrik:

Pro ty komu VS podtrhává (observableco­llection could not be found...)

ObservableCollection<Osoba>

tak do kódu musí přidat:

using System.Collections.ObjectModel;

;) (jinak mi to nefungovalo)

 
Odpovědět 9. srpna 22:07
Avatar
Honza.G
Člen
Avatar
Honza.G:

Č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. listopadu 15:29
Avatar
Odpovídá na Honza.G
Ondřej Štorc:

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 13. listopadu 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):

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. listopadu 16:13
Aj tisícmíľová cesta musí začať jednoduchým krokom.
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 14. Zobrazit vše