Lekce 6 - Programujeme pro Windows 8 - Práce se soubory
V minulé lekci, Programujeme pro Windows 8 - Zachování stavu aplikace, jsme si ukázali jakým způsobem obsluhovat základní Life-Cycle aplikace.
V dnešním tutoriálu si ukážeme několik způsobů jak číst a zapisovat data do souborů.
Každá aplikace může zasahovat pouze do předem určených složek. Nemůžeme si vynutit uložení nějakého souboru mimo vyhrazené složky bez vědomí uživatele. Pokud budeme chtít uložit soubor mimo tyto speciální složky, musíme využít tzv. Pickery (viz. níže).
Složky
Ještě jednou složky, kam můžeme zasahovat bez vědomí uživatele. Složky jsou také označovány jako Isolated Storage, tedy Izolované úložiště.
- Local Folder - složka pro soubory (.txt,.xml,obrazové soubory,...)
- Roaming Folder - to samé jako Local s tím rozdílem, že tato složka se automaticky synchronizuje přes SkyDrive pokud ho má uživatel aktivní
- Temp Folder - Složka pro dočasné soubory. Soubory po znovu otevření aplikace zmizí.
- Local Settings - Místo pro ukládání různého nastavení. Je to datový kontejner. K hodnotám přistupujeme přes Klíče. Tedy Klíč - Hodnota
- Roaming Settings - To samé jako Local Settings a opět synchronizování přes SkyDrive
Následující složky známe i z dřívějších systémů a ve WinRT jsou označovány jako KnownFolders
- Documents - Složka určená pro dokumenty
- Pictures - Složka pro obrázky
- Videos - Složka pro video
- Music - Složka pro audio
Pozn. Koho by zajímalo kde se Local / Roaming složky nacházejí tak je to zde: C:\Users\JmenoUzivatele\Local\Packages\APLIKACE. V packages mají aplikace jméno podle klíče. Musíte holt hledat
Uložení
Pojďme si tedy něco uložit. Uložíme pouze .txt soubor ve formátu CSV. Budeme mít databázi uživatelů (viz tento článek http://www.itnetwork.cz/…-souboru-csv od sdraco). Nebudu tu řešit žádný návrh aplikace a objektů. Předpokládám, že máme tedy List<Uzivatel> uzivatele. Každý má jméno, věk a datum registrace. Metoda pro uložení může vypadat následovně
public async Task<bool> UlozUzivatele() { //zíká složku Local Folder StorageFolder folder = ApplicationData.Current.LocalFolder; //vytovří nový soubor asynchronně StorageFile file = await folder.CreateFileAsync("uzivatele.txt"); try { // načte stream Stream stream = await file.OpenStreamForWriteAsync(); //založí StreamWriter ze stream /* * Můžeme místo StreamWriter použít i FileIO a metodu jako WriteTextAsync * */ using (StreamWriter writer = new StreamWriter(stream)) { //Zapíšeme uživatele foreach (Uzivatel u in uzivatele) { await writer.WriteLineAsync(string.Format("{0},{1},{2}", u.Jmeno, u.Vek, u.Registrovan.ToString("d.M.y"))); } } await stream.FlushAsync(); return true; } catch { return false; } }
Metoda je celkem prostá. Získáme složku, založíme soubor. Ze souboru vytvoříme stream a zapíšeme text. Stream poté uzavřeme. Určitě Vás ale zaráží jedna věc. Klíčová slova jako async a await už jsme jednou použili a opět vás zklamu. Dnes si to ještě nevysvětlíme. Berte to jako kouzelné slovíčko, které nám zajistí asynchronní běh metod. Celou metodu kvůli používání asynchroních metod StreamWriteru, Streamu apod musíme označit jako async! Tím říkáme, že zakládáme nové vlákno a kdekoliv je await, hlavní vlákno s UI běží (reaguje) stále dál a v pozadí se dějí další výpočty.
Především v prostředí Windows 8 aplikací se snažte o asynchronní přístup. Aplikace neprojde ani certifikací pokud vám hlavní vlákno s UI zamrzne!
Pokud async metoda potřebuje něco vracet, musí vracet objekt typu Task<T> nebo void.
Pokud chceme RoamingFolder, jednoduše načteme Roaming Folder a postup je stejný. Načítání myslím není potřeba ukazovat. Místo StreamWriter bychom použili StreamReader a ze složky bychom načetli soubor přes GetFileAsync(). Samozřejmě místo StreamWriteru / Readeru je možnost použít statickou třídu FileIO. Záleží na situaci a potřeby uložení.
XML Serializace
Formát CSV se hodí opravdu na pár skromných řádků dat. Mnohem lepší a přehlednější je XML formát. Máme zde stále možnost použít XmlReader / Writer. My si ukážeme jednodušší a rychlejší způsob, pokud chceme načíst vše a víme co přesně načítáme.
Hlavní třídou, která bude potřeba, je XmlSerializer. Třída která představuje data musí mít vždy konstruktor bez parametrů a samozřejmě klidně další s parametry. Skromná třída:
public class User { public string Name{get;set;} public int Age{get;set;} private User() {} public User(string name,int age) { this.Name = name; this.Age = age; } }
Data:
List<User> users = new List<User>(){ new User("Pepa",15), new User("Vašek",25), new User("Jarmila",15) };
A kód pro serializaci a uložení
public async Task<bool> Uloz() { try { StringWriter w = new StringWriter(); //Načteme složku StorageFolder folder = ApplicationData.Current.RoamingFolder; //Vytvoříme soubor a v případě existence přemažeme StorageFile file = await folder.CreateFileAsync("notes.xml", CreationCollisionOption.ReplaceExisting); //Založíme Serializer typu List<User> XmlSerializer srl = new XmlSerializer(typeof(List<User>)); //serializujeme srl.Serialize(w, users); //zapíšeme přes FileIO await FileIO.WriteTextAsync(file, w.ToString()); // zavřeme StreamWriter await w.FlushAsync(); return true; } catch { return false; } }
Opět myslím, že kód mluví za vše
Obdobně bude načtení:
public async Task<bool> Nacti() { try { // načteme složku StorageFolder folder = ApplicationData.Current.RoamingFolder; //načteme soubor StorageFile file = await folder.GetFileAsync("users.xml"); //založíme Serializer XmlSerializer srl = new XmlSerializer(typeof(List<User>)); // přeteme text ze souboru (ještě stále XML formát) string text = await FileIO.ReadTextAsync(file); // založíme XmlReader XmlReader r = XmlReader.Create(new StringReader(text)); //Deserializujeme a přetypujeme users = (List<User>)srl.Deserialize(r); return true; } catch { return false; } }
Vidíme, že práce s XML je velmi snadná pokud nepotřebujeme vytáhnout určité části XML
Pickery
Picker je (s trochou nepřesnosti) komponenta, která umožňuje uživateli uložit / načíst soubor nebo otevřít složku pro čtení. Je to také jediný správný postup jak uložit data mimo určené složky. Uživatel vždy musí vědět co se mu děje na zařízení (PC,tablet, ...).
Pickerů máme tedy 3 typy
- FileOpenPicker - načte jeden nebo více souborů
- FileSavePicker - uloží jeden nebo více souborů do nastavené cesty
- FolderPicker - umožní otevřít složku uvnitř aplikace
U FileOpenPicker a FolderPicker nalezneme jednu důležitou vlastnost a tím je FileTypeFilter. Jedná se o kolekci List<string> a udává jaký typ souborů můžeme otevřít / uložit. Pokud chceme všechny typy přidáme do kolekce hvězdičku() nebo určíme přesně typy souborů a to uvedením přípony: .txt,.xtml,.png,... Obdobně u *FileSavePicker nalezneme vlastnost FileTypeChoices která udává co můžeme zvolit.
Ukázka Pickeru, který načte zvolený soubor.
FileOpenPicker picker = new FileOpenPicker(); picker.FileTypeFilter.Add(".txt"); StorageFile file = await picker.PickSingleFileAsync(); IList<string> result = await FileIO.ReadLinesAsync(file, UnicodeEncoding.Utf8);
Pickery dokáží uložit i více souborů najednou. Určitě se podívejte na další nastavení Pickerů. Lze je různě stylovat
Settings
LocalSettings nebo RoamingSettings je speciální kontejner, kde můžeme snadno uchovávat hodnoty podle klíče. Je to jakýsi trvalý Dictionary. Myslím, že není zde co víc uvádět.
// uložení ApplicationData.Current.LocalSettings.Values["name"] = "Zirko"; //načtení string name = ApplicationData.Current.LocalSettings.Values["name"].ToString();
KnownFolders
Načtení a ukládání do KnownFolders je stejně jednoduché jako ostatní. Například Videos
StorageFolder videosFolder = KnownFolders.VideosLibrary;
Capabilities
S KnownFolders a s mnoha dalšími věcmi (v budoucnu si řekneme více) souvisí Package.appxmanifest. V prvním díle jsme si o tomto souboru něco málo řekli. Pokud využíváte KnownFolders, musíte zaškrtnout tu library, kterou používáte a přidat deklarace. Ovšem už překračujeme dnešní náplň článku. Detailněji si to popíšeme jindy.
Dnešní článek byl velmi teoretický, v příští lekci, Programujeme pro Windows 8 - Vlastní styly v XAML se vrhneme na něco zajímavějšího .