V minulém díle jsme si vysvětlili, jak se pracuje s poli, v tomto díle si ukážeme, jak se pracuje na disku se soubory, jak z nich čteme text a jak jej můžeme do souborů zapisovat. Ještě před tím si ale ukážeme další druh cyklu, a to cyklus While.
Cyklus While
Cyklus While se používá v případech, že potřebujeme opakovat nějaký příkaz tak dlouho, dokud se něco nestane, a jakmile se to stane, opakování skončí a pokračujeme dál. Jeho struktura vypadá následovně:
While <podmínka>
<příkazy>
End While
Řádky uvnitř cyklu se opakují, zatímco platí podmínka. Jakmile podmínka platit přestane, cyklus se ukončí a pokračuje se dalším příkazem. Nám se to bude hodit při čtení souboru. Budeme totiž číst postupně po řádcích, dokud je nepřečteme všechny. Rozdíl mezi For cyklem a While cyklem je mimojiné v tom, že u For cyklu ještě před jeho zahájením víme, kolikrát se zopakuje. Nemusíme znát konkrétní číslo, ale musíme mít počet opakování v nějaké proměnné. U While cyklu ovšem nevíme, kolik řádků soubor má (a to se rychle zjistit nedá). Zkrátka čteme, dokud nenarazíme na konec souboru. Je to jako když zatloukáte hřebík - dáváte údery kladivem, dokud není hřebík zatlučený, zkrátka opakujete akci úderu kladiva, dokud platí podmínka, že hřebík ještě není zatlučený.
Práce se soubory
Soubory můžeme dělit do různých skupin, nás však zajímá dělení podle typu dat uvnitř souboru. Pokud to velmi zjednoduším, soubory se dělí na textové a binární. Zatím budeme pracovat se soubory textovými, které (jak ostatně z jejich názvu plyne) obsahují text.
Příjmy a výdaje
Jak asi správně tušíte, napíšeme si velmi jednoduchou aplikaci pro příjmy a výdaje domácnosti. Při tom se naučíme pracovat s komponentou ListView, což je seznam s rozšířenými možnostmi. Data si při startu načteme a při ukončení programu uložíme do textového souboru.
Spusťte si tedy Visual Basic, vytvořte novou Windows Application a v průzkumníkovi projektu klikněte na název projektu pravým tlačítkem a vyberte Add / New Item.... V dialogovém okně pro výběr typu položky vyberte Text File a pojmenujte nový soubor jako database.txt. Po jeho přidání na něj v průzkumníkovi projektu klikněte a jeho vlastnost Copy To Output Directory nastavte na Copy If Newer. Výsledný program se totiž spouští z jiného umístění než máme soubory projektu, tímto nastavením zajistíme, že se tam zkopíruje vždy, když bude třeba.
Nyní na tento soubor dvakrát klikněte, aby se nám otevřel v editoru. Zkopírujte do něj obsah tohoto pole:
10.1.2007
42758
Výplata
13.1.2007
-578
Platba v supermarketu
15.1.2007
-3000
Výběr z bankomatu
19.1.2007
-1000
Výběr z bankomatu
21.1.2007
1200
Moje narozeniny
23.1.2007
-1000
Elektřina
23.1.2007
-5000
Výběr z bankomatu
24.1.2007
-2000
Výběr z bankomatu
24.1.2007
-340
Plyn - záloha
25.1.2007
-1000
Výběr z bankomatu
26.1.2007
-799
Platba za oblečení
26.1.2007
-600
Voda - záloha
27.1.2007
-11537
Hypotéka
Každý záznam je v souboru zapsán na třech řádcích. První řádek obsahuje datum, druhý částku (kladná = příjmy, záporná = výdaje) a třetí popis finanční operace.
Na formulář si přidejte komponentu ListView. Jak jsem již psal před chvílí, jedná se o rozšířený ListBox, který podporuje obrázky a více sloupců pro jeden záznam, kromě toho dokáže také zobrazit položky jako ikony, stejně jako Průzkumník Windows. Pod tuto komponentu přidejte 3 tlačítka - Přidat, Upravit a Odstranit. Pomocí nich budeme později manipulovat se záznamy v aplikaci.
Načtení dat do seznamu
Načtení dat probíhá při spouštění aplikace, dvakrát tedy klikněte na plochu formuláře (ne na žádnou komponentu ani na titulkový pruh okna). Měla by se vám vytvořit procedura Form1_Load. Pokud ne, kliknuli jste špatně (asi jste měli vybranou nějakou komponentu).
Nyní se musíme naučit pracovat se soubory. Pro čtení souboru použijeme objekt StreamReader. A co že to ten objekt je? No o tom by se dal napsat celý článek. Nám bude stačit, když si prozatím zapamatujeme, že je to jakýsi soubor funkcí, procedur a vlastností, od kterého můžeme vytvořit několik exemplářů (instancí), které jsou na sobě nezávislé. Typickým objektem je třeba tlačítko. Na formulář můžeme umístit 30 tlačítek, která jsou prakticky vzato stejná. Akorát mají každé trochu jiné vlastnosti, takže na každém je jiný text, každé má jinou pozici a třeba i velikost, barvu atd. Ale v principu je to tlačítko a všechna tlačítka dělají to samé. Každé tlačítko je ale na formuláři samostatné, pokud změníme jedno tlačítko, ostatní zůstanou stejná.
Objekt se deklaruje podobně jako proměnná, akorát za slovo As musíme dát ještě slovíčko New, které říká, že se jedná o objekt. Objekty také provádí většinou složitější funkce. Objekt StreamReader slouží ke čtení textových souborů. Vše, co pracuje se soubory, najdeme většinou ve jmenném prostoru System.IO. Jmenné prostory jsou něco jako kategorie nebo složky - oddělují se tečkou a třídí nám všechny součásti .NET Frameworku podle toho, co dělají. Pokud chceme tedy přistupovat k objektu StreamReader, musíme napsat IO.StreamReader. Standardně totiž nemáme System.IO naimportovaný, importy se naučíme později.
Objekt StreamReader má několik metod (což je procedura nebo funkce, která pracuje s tímto objektem). Pro nás je důležitá metoda ReadLine, která přečte ze souboru jeden řádek. Pokud tedy vytvoříme tento objekt a zavoláme tuto metodu, vrátí nám první řádek v souboru. Pokud ji zavoláme znovu, vrátí druhý řádek atd. a tak to pokračuje, dokud nedojdeme na konec souboru. Tento objekt má také vlastnost EndOfStream, která má ze začátku hodnotu False a pokud dojdeme na konec souboru, změní se její hodnota na True. A to je právě podmínka, kterou potřebujeme ve While cyklu. Po přečtení souboru nesmíme zapomenout tento soubor zavřít, aby se zpřístupnil pro ostatní aplikace, a to zavoláním metody Close. K metodám přistupujeme stejně jako k vlastnostem a vlastnosti už známe. Napíšeme název objektu, tečku a pak název metody nebo vlastnosti, se kterou chceme pracovat.
Otevření a zavření souboru vypadá takto (samotné čtení obsahu musí být mezi těmito dvěma řádky):
Dim soubor As New IO.StreamReader("database.txt")
soubor.Close()
Vytvoření objektu tedy musí obsahovat klíčové slovo New. Některé objekty vyžadují při vytváření ještě zadání nějakých parametrů (ty se píší do závorky a oddělují čárkou, stejně jako u funkcí či procedur).
Upozorňuji raději znovu, že soubor je třeba po dokončení práce s ním zavřít! Rozhodně není dobré spoléhat na to, že s ukončením aplikace se soubor odemkne a bude přístupný ostatním!
Nyní sestavíme While cyklus. Objekt StreamReader má vlastnost EndOfStream, která bude celou dobu False a na konci souboru se změní na True. Podmínka While cyklu ovšem musí mít celou dobu hodnotu True (musí platit) a jakmile se v ní objeví False (platit přestane), cyklus končí. Opakovat tedy musíme, zatímco soubor.EndOfStream = False.
Pokud si představíme podmínku a = 6, její význam je jasný. Výsledkem tohoto výrazu je ale hodnota True nebo False. Pokud ale pracujeme s proměnnou typu Boolean, která True nebo False vrací sama o sobě, nemusíme psát a = True, stačí pouze napsat a. Pokud potřebujeme opačný případ, tedy a = False, lze napsat i Not a. Je prakticky jedno, který způsob si vyberete, ale zvyklostí bývá používat a a Not a, je to méně psaní. I já se toho tedy budu držet, je to čistě věc zvyku. Podmínkou našeho While cyklu tedy nebude (i když by mohlo být, funguje to stejně) soubor.EndOfStream = False, ale Not soubor.EndOfStream. Zvláště při čtení souboru až do konce se v drtivché většině případů (i v jiných jazycích) používá raději operátor Not. Je to taková "ustálená konstrukce".
Do předchozího kódu tedy vložíme cyklus a bude to vypadat nějak takhle:
Dim soubor As New IO.StreamReader("database.txt")
While Not soubor.EndOfStream
Dim datum As DateTime = CDate(soubor.ReadLine())
Dim castka As Double = CDbl(soubor.ReadLine())
Dim text As String = soubor.ReadLine()
End While
soubor.Close()
Nás teď budou zajímat tři řádky uvnitř cyklu. Zakládáme v nich tři proměnné a do každé vložíme jeden řádek. Nejprve načítáme datum (pokud jsme si to ještě neříkali, tak si zapamatujte, že pro datum máme datový typ DateTime). Převodní funkce na datum je CDate. Zavolání metody soubor.ReadLine() vrátí další řádek v souboru.Na druhém řádku je částka, což je číslo (částka může být desetinná, proto Double), načtený řádek musíme opět převést. Třetí řádek načteme normálně, je to text. Převádět tedy nemusíme.
Nastavení komponenty ListView
Než použijeme komponentu ListView jako seznam, musíme jí změnit některé vlastnosti. Nastavte jí tedy vlastnost View na hodnotu Details a poklepejte na vlastnost Columns. V editoru kolekcí přidejte 3 sloupečky (tlačítkem Add) a pokud jeden z těchto sloupečků vyberete, můžete mu nastavovat vlastnosti. Prvnímu tedy nastavte hodnotu vlastnosti Text na Datum, druhému na Částka a třetímu na Popis. Pak klikněte na OK. Roztáhněte ještě sloupečky tak, aby to vypadalo pěkně.
Přidávání položek do ListView
Komponenta ListView je o něco složitější než ListBox. Jednotlivé položky tohoto seznamu jsou objekty ListViewItem. Pro každou načtenou trojici musíme vytvořit nový objekt této položky seznamu. Objekt ListViewItem má mnoho vlastností, jednou z nich je Text, což je obsah prvního sloupečku. Do něj vložíme datum, automaticky se převede na String. Pokud chceme přidat hodnotu dalšího sloupečku, přidáme ji do vlastnosti SubItems metodou Add. Parametrem je již jen text sloupečku. Postupně tedy přidáme nejprve částku a pak popis. Nakonec nově vytvořenou položku ListViewItem musíme přidat do daného seznamu (nepřidá se sama, pokud by na formuláři bylo více seznamů, nevěděli bychom, do kterého). Přidání provedeme opět předáním položky metodě Add vlastnosti Items komponenty ListView.
Celý kód procedury tedy bude vypadat takto (jiný tam zatím nemáme):
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim soubor As New IO.StreamReader("database.txt")
While Not soubor.EndOfStream
Dim datum As DateTime = CDate(soubor.ReadLine())
Dim castka As Double = CDbl(soubor.ReadLine())
Dim text As String = soubor.ReadLine()
Dim polozka As New ListViewItem()
If castka < 0 Then polozka.ForeColor = Color.Red
polozka.Text = datum
polozka.SubItems.Add(castka)
polozka.SubItems.Add(text)
ListView1.Items.Add(polozka)
End While
soubor.Close()
End Sub
Pokud program nyní spustíte, uvidíte načtené položky. Výdaje se obarví na červeno kvůli podmínce, která nastaví v případě částky nižší než 0 hodnotu vlastnosti ForeColor na červenou (barva textu).
Uložení dat
V tomto díle ještě uložíme data a zbytek dokončíme v díle příštím. Abych vás zbytečně nezahlcoval.
Nyní již počet položek známe, takže můžeme provést cyklus For. Počet položek je k mání v ListView1.Items.Count. Položky se ale číslují od nuly (jako ostatně všechno v .NETu). Zápis se provádí pomocí objektu StreamWriter, nový řádek přidáme metodou WriteLine. Opět nezapomeňte soubor zavřít.
V editoru kódu nahoře vyberte v prvním rozbalovacím seznamu položku Form1 Events a ve druhém událost FormClosing, která se spouští během uzavírání formuláře.
Jednotlivé položky jsou uloženy v ListView1.Items, což je tzv. kolekce. Chová se podobně jako pole, ale nemá omezenou velikost a má metodu Add, kterou můžeme položky přidávat na konec. První položku tedy najdeme pod ListView1.Items(0). Každá položka má v sobě svoji kolekci SubItems, což jsou jednotlivé sloupečky dané položky. První obsahuje datum, sruhý částku a třetí popis. Jednoduše je tedy vypíšeme:
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
Dim soubor As New IO.StreamWriter("database.txt")
For i As Integer = 0 To ListView1.Items.Count - 1
soubor.WriteLine(ListView1.Items(i).SubItems(0).Text)
soubor.WriteLine(ListView1.Items(i).SubItems(1).Text)
soubor.WriteLine(ListView1.Items(i).SubItems(2).Text)
Next
soubor.Close()
End Sub
Celý program vypadá takto:
V příštím díle naprogramujeme tlačítka a umožníme upravovat, přidávat a odstraňovat záznamy. Doufám, že jsou vám po dnešku jasné oba dva cykly, a pokud ne, napište, abych věděl, jestli mám vysvětlovat více nebo to takhle stačí. S objekty si nelamte hlavu, pro začátečníky je to komplikovanější, ale po pár článcích to jistě pochopíte.