Letní akce PHP týden
Pouze tento týden sleva až 80 % na kurzy PHP. Lze kombinovat s akcí Letní slevy na prémiový obsah!
Brno? Vypsali jsme pro vás nové termíny školení Základů programování a OOP v Brně!

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

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 C# .NET 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:

  • 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.

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 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 ObservableCollection. Asi si dokážete 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 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.
  • A 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 v LINQ 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 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. 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 ovládacím prvkem DatePicker dostaneme datum jako typ DateTime?. Pokud jste dočetli objektový kurz C# 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 ObservableCollection. 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 715x (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?
22 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 sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Předchozí článek
Upomínač narozenin v C# .NET WPF - Návrh oken
Všechny články v sekci
Okenní aplikace v C# .NET WPF
Miniatura
Následující článek
Upomínač narozenin v C# .NET WPF - Propojení vrstev
Aktivity (6)

 

 

Komentáře

Avatar
Raxume
Redaktor
Avatar
Raxume:20.12.2013 21:16

Ahoj, mohl bych se zeptat proč je definována proměnná dnes a později není použita?

public int ZbyvaDni
{
        get
        {
                **DateTime dnes = DateTime.Today;**
                DateTime dalsiNarozeniny = Narozeniny.AddYears(Vek + 1);

                TimeSpan rozdil = dalsiNarozeniny - *DateTime.Today*;

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

Díky

Raxume

 
Odpovědět  +2 20.12.2013 21:16
Avatar
D36
Člen
Avatar
D36:9.2.2014 11:33

Ahoj,chtěl jsem se zeptat,že při použití ObservableCollec­tion je nutné použít "using System.Collec­tions.ObjectMo­del;" jinak dojde k chybě.V návodu to není zmíněno. Je tato část "using System.Collec­tions.ObjectMo­del;" generována automaticky a nebo je nutné ji vždy doplnit ručně?

 
Odpovědět  +5 9.2.2014 11:33
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na D36
Jan Vargovský:9.2.2014 12:32

Když už člověk začíná s WPFkem, měl by vědět jak přidat daný using. Ale ano, máš pravdu, musí se přidat.

 
Odpovědět 9.2.2014 12:32
Avatar
Filistin
Člen
Avatar
Filistin:25.11.2015 19:52

Dal by se zápis

var serazeneOsoby = Osoby.OrderBy(o => o.ZbyvaDni);

nějak zapsat do toho zápisu podobném SQL - jako je to v té sekci Kolekce a LINQ v C# .NET, na kterou odkazujete?

Je to spíše pro zajímavost.
Díky

 
Odpovědět 25.11.2015 19:52
Avatar
Filistin
Člen
Avatar
Odpovídá na Filistin
Filistin:25.11.2015 19:55

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:4.4.2016 13:23

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.4.2016 13:23
Programátor je stroj k převodu kávy na kód.
Avatar
Odpovídá na krepsy3
Michal Štěpánek:4.4.2016 14:00

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.4.2016 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:4.4.2016 20:03

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.4.2016 20:03
Programátor je stroj k převodu kávy na kód.
Avatar
Odpovídá na krepsy3
Michal Štěpánek:4.4.2016 23:09

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

Editováno 4.4.2016 23:11
Odpovědět 4.4.2016 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.4.2016 7:04
Programátor je stroj k převodu kávy na kód.
Avatar
prokop.mejstrik:9.8.2016 22:07

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  +3 9.8.2016 22:07
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
Odpovídá na prokop.mejstrik
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.6.2017 21:28

Ve observateColection není addRange?

Odpovědět 15.6.2017 21:28
#c#
Avatar
Marek
Člen
Avatar
Marek:13.9.2017 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.9.2017 23:33
Avatar
David Oczka
Redaktor
Avatar
Odpovídá na Marek
David Oczka:14.9.2017 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.9.2017 8:21
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Marek
David Čápka:14.9.2017 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.9.2017 10:09
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Marek
Člen
Avatar
Odpovídá na David Oczka
Marek:14.9.2017 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.9.2017 21:02
Avatar
Jan Troják
Člen
Avatar
Jan Troják:25.11.2017 22:05

Nalezena chyba: v kodu ZbyvaDni je zbytečně vytvořená instance: 'DateTime dnes = DateTime.Today;'

Původní:

public int ZbyvaDni
{
        get
        {
                DateTime dnes = DateTime.Today;
                DateTime dalsiNarozeniny = Narozeniny.AddYears(Vek + 1);

                TimeSpan rozdil = dalsiNarozeniny - DateTime.Today;

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

Opravené:

public int ZbyvaDni
{
        get
        {
                DateTime dalsiNarozeniny = Narozeniny.AddYears(Vek + 1);

                TimeSpan rozdil = dalsiNarozeniny - DateTime.Today;

                return Convert.ToInt32(rozdil.TotalDays);
        }
}
 
Odpovědět 25.11.2017 22:05
Avatar
Odpovídá na Jan Troják
Michal Štěpánek:26.11.2017 10:01

Zbytečně vytvořená sice je, ale že by to byla nějaká ukrutná chyba, se mi nezdá...
stačí jen opravit toto

TimeSpan rozdil = dalsiNarozeniny - DateTime.Today;

na toto

TimeSpan rozdil = dalsiNarozeniny - dnes;
Editováno 26.11.2017 10:02
Odpovědět  +1 26.11.2017 10:01
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Martin Komínek:3.1.2018 17:16

Ahoj, dnes když jsem napsal ObservableCollec­tion ve Visual Studiu, tak to tento typ kolekce neznalo. Pokud by jste měli podobný problém stačí nahoře napsat. using System.Collec­tions.ObjectMo­del.

 
Odpovědět  +2 3.1.2018 17:16
Avatar
Odpovídá na Martin Komínek
Krystof Matejka:18.3.2018 10:55

Díky, měl jsem podobný problém, ušetřilo mi to nějaký čas.

 
Odpovědět 18.3.2018 10:55
Avatar
Odpovídá na Martin Komínek
Michal Štěpánek:18.3.2018 21:49

Stačí tam tu "kolekci" napsat a po najetí myši na červeně podtržené slovo se objeví možnosti řešení, mezi kterými je i přidání potřebných usingů...

Odpovědět  +1 18.3.2018 21:49
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
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 26 zpráv z 26.