Dieser Artikel ist Tag #16 der Serie 31 Tage Mango von Jeff Blankenburg.
Der Originalartikel befindet sich hier: Day #16: Isolated Storage Explorer.
Dieser Artikel wurde in der Originalserie von Gastautor Samidip Basu geschrieben. Bei Twitter kann Samidip unter @samidip erreicht werden.
Im Windows Phone SDK 7.1 ist ein neues Werkzeug für Entwickler enthalten: der Isolated Storage Explorer. In diesem Artikel wollen wir uns ansehen, wie man den Isolated Storage Explorer verwendet und wie er beim Test von Anwendungen, die Daten im Isolated Storage ablegen, helfen kann. Der Isolated Storage Explorer funktioniert sowohl mit Anwendungen, die die Windows Phone 7.1 Runtime verwenden, als auch mit Anwendungen für Windows Phone 7.0.
Die Grundlagen
Isolated Storage ist für Windows Phone Anwendungen die Lösung, Daten persistent und nur für die Anwendung sichtbar („Sandboxed“) zu speichern (Jeff hat das an Tag #15 der Original 31 Days of Windows Phone behandelt). Die Abstraktion vom darunter liegenden Dateisystem des Betriebssystems hat für 3rd-Party Anwendungen den Vorteil der Sichherheit und Zuverlässigkeit. Bei der Ablage von Daten im Isolated Storage ist es wichtig, dass wir erstens die richtigen Daten ablegen und zweitens, dass wir den Fußabdruck der Anwendung möglichst klein halten, also nicht benötigte Daten wieder löschen, etc.. Hierbei kann der Isolated Storage Explorer helfen.
Die Verwendung von Isolated Storage kann auf drei unterschiedliche Weisen erfolgen:
- Speicherung von Name-Wert-Paaren durch die Klasse IsolatedStorageSettings.
- Speicherung von Ordnern und Dateien im Isolated Storage.
- Verwendung von SQL CE um relationale Daten im Isolated Storage abzulegen.
Viele Anwendungen verwenden die zweite Möglichkeit der Ablage beliebiger Dateien und Ordner im Isolated Storage. Da der Windows Phone Emulator eine VM ist, war es vor dem Windows Phone Mango SDK 7.1 etwas umständlich, die Korrektheit der Ordner- und Dateistruktur im Isolated Storage zu überprüfen.
Mit dem neuen SDK haben wir das Isolated Storage Explorer Werkzeug bekommen, mit dem es möglich ist, die Dateien und Verzeichnisse im Isolated Storage anzusehen und zu manipulieren. Das funktioniert sowohl für Isolated Storage im Emulator als auch für entsperrte Entwicklergeräte. Dieses Kommandozeilenwerkzeug erlaubt die Auflistung von Dateien im der Wurzel des Isolated Storage der Anwendung aber auch in Unterverzeichnissen, die von der Anwendung zur Laufzeit erstellt wurden. Zudem können wir den Inhalt des Isolated Storage aus dem Emulator oder dem Gerät in einen Ordner auf dem Computer transferieren. Dort können wir Dateien oder Verzeichnisse verändern und die geänderten Daten wieder zurückspielen. Erstens können wir damit die von der Anwendung angelegten Strukturen überprüfen. Zweitens können wir das Verhalten der Anwendung — z.B. in Randfällen — testen, indem wir die Daten im Isolated Storage manipulieren und wieder in den Emulator oder auf das Gerät übertragen. Doch genug der Vorrede — lassen Sie uns beginnen!
Unsere Beispielanwendung
In unserer Demo Windows Phone Anwendung werden wir einige Beispieldateien in der Wurzel des Isolated Storage anlegen. Außerdem werden wir einige Ordner erstellen und auch dort ein paar Beispieldateien ablegen. Wir beginnen diesmal mit der Projektvorlage für eine Pivot Anwendung. Im ersten Bereich werden wir die Dateien in der Wurzel anzeigen, im zweiten die Unterordner mit Dateien und der dritte Bereich wird eine kleine Überraschung.
Wenn die Anwendung zum ersten Mal startet, rufen wir eine kleine Methode, die die Dateien und Verzeichnisse im Isolated Storage für uns anlegt. Im Konstruktor unserer App.xaml.cs setzen wir also ein globales Flag, welches uns anzeigen wird, ob wir die Initialisierung durchführen müssen:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Navigation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; namespace Day16_IsolatedStorageExplorer { public partial class App : Application { public PhoneApplicationFrame RootFrame { get; private set; } public bool FirstLaunch; // Constructor public App() { // All the automatically-added code... // Set the flag for fresh App launches. this.FirstLaunch = true; } // Everything else ... } }
Unsere Anwendung braucht immer noch eine Methode, die den Isolated Storage beim ersten Start mit Dateien und Verzeichnissen füllt. Im folgenden Codebeispiel speichern wir also einige Beispieldateien und ‑Verzeichnisse. Wenn Sie schon eine Anwendung haben, die Inhalte im Isolated Storage ablegt, können Sie diesen Schritt auch weglassen. Beachten Sie die Verwendung des Namespaces System.IO.IsolatedStorage. Diesen brauchen wir, um auf die Klassen IsolatedStorageFile und IsolatedStorageFileStream zuzugreifen. Diese wiederum brauchen wir, um unsere Beispieldateien zu schreiben.
using System; using System.IO; using System.IO.IsolatedStorage; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; namespace Day16_IsolatedStorageExplorer { public partial class MainPage : PhoneApplicationPage { IsolatedStorageFile fileStorage; // Constructor public MainPage() { InitializeComponent(); // Create a reference to the App's Isolated Storage fileStorage = IsolatedStorageFile.GetUserStoreForApplication(); // Create initial sample Files & Directories, for fresh App Launch only. if ((App.Current as Day16_IsolatedStorageExplorer.App).FirstLaunch) this.CreateInitialFilesAndDirectories(); // Reset the flag. (App.Current as Day16_IsolatedStorageExplorer.App).FirstLaunch = false; } private void CreateInitialFilesAndDirectories() { // Create a new StreamWriter, to write files to the root directory. StreamWriter fileWriter = new StreamWriter(new IsolatedStorageFileStream("RootFile1.txt", FileMode.OpenOrCreate, fileStorage)); // Write sample data. fileWriter.WriteLine("This is test data."); // Close the StreamWriter. fileWriter.Close(); // Repeat. fileWriter = new StreamWriter(new IsolatedStorageFileStream("RootFile2.txt", FileMode.OpenOrCreate, fileStorage)); fileWriter.WriteLine("This is test data."); fileWriter.Close(); // Create 2 new SubDirectories. fileStorage.CreateDirectory("SubDirectory1"); fileStorage.CreateDirectory("SubDirectory2"); // Put sample files in them. fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory1\\SubDir1File1.txt", FileMode.OpenOrCreate, fileStorage)); fileWriter.WriteLine("This is test data."); fileWriter.Close(); fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory1\\SubDir1File2.txt", FileMode.OpenOrCreate, fileStorage)); fileWriter.WriteLine("This is test data."); fileWriter.Close(); fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory2\\SubDir2File2.txt", FileMode.OpenOrCreate, fileStorage)); fileWriter.WriteLine("This is test data."); fileWriter.Close(); } } }
Um mehr über die Möglichkeiten der Dateisystemoperationen mit den Klassen IsolatedStorageFile und IsolatedStorageFileStream zu erfahren, können Sie in der MSDN Dokumentation hier und hier nachlesen.
So, jetzt haben wir einige Beispieldateien in der Wurzel des Isolated Storage und zudem einige Verzeichnisse, die ebenfalls Beispieldateien enthalten. Lassen Sie uns jetzt den Inhalt des Isolated Storage an der Benutzeroberfläche ausgeben. Indem wir den Inhalt des Isolated Storage dynamisch ausgeben, können wir später die Änderungen überprüfen, die wir mit Hilfe des Isolated Storage Explorer Werkzeugs an den Daten vornehmen werden. Damit der heutige Artikel nicht zu lang wird, überspringen wir den XAML Code für die Benutzeroberfläche. Er ist aber in der Beispielanwendung enthalten, die Sie unten herunterladen können. Folgende Dinge fügen wir hier hinzu: eine kleine Klasse DirectorySystem, die eine Liste unserer Verzeichnisse verwalten und uns beim Binding helfen wird, den Code zum Scannen der Dateien und Verzeichnisse und die Bindings an die beiden ListBoxes RootListBox und DirectorySystemListBox im Pivot Control. Hier ist also die modifizierte code-behind Datei für die Main Page:
using System; using System.IO; using System.IO.IsolatedStorage; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; namespace Day16_IsolatedStorageExplorer { public partial class MainPage : PhoneApplicationPage { IsolatedStorageFile fileStorage; string[] rootFiles; string[] directories; string[] directoryFiles; List<DirectorySystem> listDirectorySystem = new List<DirectorySystem>(); // Constructor public MainPage() { InitializeComponent(); // Create a reference to the App's Isolated Storage fileStorage = IsolatedStorageFile.GetUserStoreForApplication(); // Create initial sample Files & Directories, for fresh App Launch only. if ((App.Current as Day16_IsolatedStorageExplorer.App).FirstLaunch) this.CreateInitialFilesAndDirectories(); // Reset the flag. (App.Current as Day16_IsolatedStorageExplorer.App).FirstLaunch = false; // Read root files. rootFiles = fileStorage.GetFileNames(); this.RootListBox.ItemsSource = rootFiles; // Read Directories & build File system. directories = fileStorage.GetDirectoryNames(); foreach (string dir in directories) { directoryFiles = fileStorage.GetFileNames(dir + "\\*"); DirectorySystem newDir = new DirectorySystem(); newDir.DirectoryName = dir; newDir.DirectoryFiles = directoryFiles; listDirectorySystem.Add(newDir); } // Bind to UI. this.DirectorySystemListBox.ItemsSource = listDirectorySystem; } private void CreateInitialFilesAndDirectories() { // Create a new StreamWriter, to write files to the root directory. StreamWriter fileWriter = new StreamWriter(new IsolatedStorageFileStream("RootFile1.txt", FileMode.OpenOrCreate, fileStorage)); // Write sample data. fileWriter.WriteLine("This is test data."); // Close the StreamWriter. fileWriter.Close(); // Repeat. fileWriter = new StreamWriter(new IsolatedStorageFileStream("RootFile2.txt", FileMode.OpenOrCreate, fileStorage)); fileWriter.WriteLine("This is test data."); fileWriter.Close(); // Create 2 new SubDirectories. fileStorage.CreateDirectory("SubDirectory1"); fileStorage.CreateDirectory("SubDirectory2"); // Put sample files in them. fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory1\\SubDir1File1.txt", FileMode.OpenOrCreate, fileStorage)); fileWriter.WriteLine("This is test data."); fileWriter.Close(); fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory1\\SubDir1File2.txt", FileMode.OpenOrCreate, fileStorage)); fileWriter.WriteLine("This is test data."); fileWriter.Close(); fileWriter = new StreamWriter(new IsolatedStorageFileStream("SubDirectory2\\SubDir2File2.txt", FileMode.OpenOrCreate, fileStorage)); fileWriter.WriteLine("This is test data."); fileWriter.Close(); } } public class DirectorySystem { public string DirectoryName { get; set; } public string[] DirectoryFiles { get; set; } } }
Falls Ihnen das ein bisschen zuviel Code ist, laden Sie doch einfach den kompletten Source Code ganz unten im Artikel herunter und probieren Sie die Anwendung selbst aus. Wenn wir die Anwendung auf dem Emulator installieren, wird unsere obige Initialisierungsmethode aufgerufen, um die Beispieldaten im Isolated Storage zu speichern. Als nächstes wird der Code ausgeführt, der den Isolated Storage nach Dateien und Ordnern durchsucht und die Ergebnisse an die Benutzeroberfläche bindet. Wenn alles funktioniert, können Sie einfach F5 drücken um die Anwendung zu starten. Das sollte dann ungefähr so aussehen:
Wir können jetzt in der Anwendung die Dateien im Isolated Storage ansehen. Als nächstes wollen wir die Dateien auch öffnen, den Inhalt betrachten und bearbeiten. Auf diese Weise können wir den Inhalt von Dateien nach der Bearbeitung prüfen und zudem testen, wie sich die Anwendung verhält, wenn wir Dateien außerhalb des Emulators modifizieren. Um die Bearbeitung zu realisieren, fügen wir unserem Projekt eine zweite einfache XAML Seite hinzu, die uns zur Betrachtung und Bearbeitung einzelner Dateien dienen wird. Hier ist die angestrebte Benutzeroberfläche:
Um die Seite zu einer einzelnen Datei zu öffnen, registrieren wir einen MouseLeftButtonUp Event Handler für die RootListBox unserer Hauptseite um festzustellen, welche Datei der Anwender betrachten möchte. Als nächstes fügen wir den folgenden kleinen Codeausschnitt im code-behind hinzu:
private void RootListBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (this.RootListBox.SelectedItem != null) { string selectedFileName = this.RootListBox.SelectedItem.ToString(); this.NavigationService.Navigate(new Uri("/FileContent.xaml?FileName=" + selectedFileName, UriKind.Relative)); this.RootListBox.SelectedItem = null; } }
Im Wesentlichen übergeben wir einfach den Dateinamen der gewählten Datei an die zweite XAML Seite. Schauen wir uns jetzt an, wie wir die Datei in der neuen Seite lesen, zur Bearbeitung öffnen und schließlich die Änderungen des Anwenders speichern:
using System; using System.IO; using System.IO.IsolatedStorage; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; namespace Day16_IsolatedStorageExplorer { public partial class FileContent : PhoneApplicationPage { string currentFileName; private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { if (this.NavigationContext.QueryString.ContainsKey("FileName")) { currentFileName = this.NavigationContext.QueryString["FileName"]; this.actualFileName.Text = currentFileName; this.ReadFileData(currentFileName); } } private void btnSave_Click(object sender, EventArgs e) { this.EditFileData(currentFileName); } private void ReadFileData(string filePath) { using (IsolatedStorageFile appIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication()) { if (appIsolatedStorage.FileExists(filePath)) { using (IsolatedStorageFileStream fileStream = appIsolatedStorage.OpenFile(filePath, FileMode.Open, FileAccess.Read)) { using (StreamReader reader = new StreamReader(fileStream)) { this.fileContent.Text = reader.ReadLine(); } } } } } private void EditFileData(string filePath) { using (IsolatedStorageFile appIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication()) { if (appIsolatedStorage.FileExists(filePath)) { using (IsolatedStorageFileStream fileStream = appIsolatedStorage.OpenFile(filePath, FileMode.Open, FileAccess.Write)) { using (StreamWriter writer = new StreamWriter(fileStream)) { string editedText = this.fileContent.Text.Trim(); writer.Write(editedText); writer.Close(); } } } } this.NavigationService.Navigate(new Uri("/DirectoryListings.xaml", UriKind.Relative)); } } }
Wenn die Seite lädt, lesen wir den Dateinamen aus dem Query Parameter, den wir von der Pivot Page übergeben bekommen haben. Mit dem Dateinamen und einer Referenz auf den Isolated Storage der Anwendung liest die Methode ReadFileData den Inhalt der Datei im lesbaren Modus zur Anzeige an der Oberfläche ein. Was passiert, wenn der Anwender Änderungen am Dateiinhalt vornimmt und den Save Button drückt? Dann rufen wir die Methode EditFileData auf, welche die Datei öffnet — diesmal allerdings im schreibbaren Modus. Sie verwendet dann ein Objekt vom Typ IsolatedStorageFileStream um den aktualisierten Inhalt zurück in die Datei zu schreiben.
Wir sind jetzt so weit, dass wir Beispieldateien im Isolated Storage erstellen, betrachten und bearbeiten können. Weiterhin haben wir sogar eine einfache Verzeichnisstruktur angelegt. Das ist alles schon mal nicht schlecht — wir hätten aber etwas mehr Sicherheit, wenn wir uns die im Emulator oder dem Gerät erstellten Strukturen und Inhalte direkt anschauen könnten. Hier kommt der Isolated Storage Explorer ins Spiel!
Verwendung des Isolated Storage Explorers
Der Isolated Storage Explorer ist ein Kommandozeilenwerkzeug, welches im Windows Phone SDK 7.1 enthalten ist. Das Werkzeug heißt ISETool.exe und befindet sich normalerweise im Verzeichnis Program Files\Microsoft SDKs\Windows Phone\v7.1\Tools\IsolatedStorageExplorerTool. Lassen Sie uns also die Kommandozeile starten und in das obige Verzeichnis wechseln um das Werkzeug zu starten.
Das ISE Werkzeug kann in den Isolated Storage des Emulators oder eines entsperrten Entwicklergeräts schauen. Die Anwendung, deren Isolated Storage Sie betrachten möchten, muss auf dem laufenden Emulator installiert sein. Sie muss aber selbst nicht laufen, da der Sinn des Isolated Storage genau die Persistenz ist. Wie jedes andere Kommandozeilenwerkzeug gibt es für das ISE Werkzeug einige Kommandozeilenschalter, die das Verhalten steuern. Die generelle Syntax ist wie folgt. Auf die einzelnen Optionen gehen wir noch im Detail ein.
ISETool.exe <ts|rs|dir[:device-folder]> <xd|de> <Product GUID> [<computer-path>]
Sobald unsere Beispielanwendung einmal gestartet wurde, befinden sich Dateien und Verzeichnisse im Isolated Storage. Angenommen, die Anwendung läuft gerade nicht und wir wollen die Daten im Isolated Storage nur mal ansehen. Das Kommando hierfür ist:
ISETool.exe dir xd 04ce6350-faf0-4977–8e5b-fb288fc127c0
Die Bestandteile im Einzelnen sind:
- ISETool.exe = das Kommandozeilenprogramm selbst.
- dir = listet alle Dateien und Verzeichnisse unterhalb des angegebenen Verzeichnisses auf (oder unterhalb der Wurzel, wenn kein Verzeichnis angegeben wird).
- xd = damit geben wir an, dass wir den Isolated Storage des Emulators betrachten wollen.
- de = mit de anstatt xd würden wir angeben, dass wir das selbe Kommando — nur für den Isolated Storage auf dem echten Gerät — ausführen möchten.
- <Product GUID> = das ist die Application ID unserer Anwendung, so dass das Werkzeug weiß, in welchen Bereich des Isolated Storage es schauen soll. Die Product GUID zu jeder Anwendung steht in der Datei WMAppManifest.xml.
Mit diesem Befehl gibt unser Kommandozeilen-Helferlein alle Beispieldateien in der Wurzel des Isolated Storage und alle von uns angelegten Unterverzeichnisse aus. Wollen Sie den Inhalt eines der Unterverzeichnisse ansehen? Hier ist das Kommand:
ISETool.exe dir:„SubDirectory1“ xd 04ce6350-faf0-4977–8e5b-fb288fc127c0
Alles an dem Kommando bleibt gleich. Wir geben lediglich ein Verzeichnis an, dessen Inhalt ausgegeben werden soll.
Kommen wir zum wirklich interessanten Teil! Hier ist das Kommando, um den Inhalt des Isolated Storage vom Emulator oder vom Gerät in ein Verzeichnis auf dem PC zu kopieren:
ISETool.exe ts xd 04ce6350-faf0-4977–8e5b-fb288fc127c0 „C:\Users\Sami\Desktop“
Zwei Dinge sind hier neu:
- ts = Take Snapshot („Abzug machen“). Wie der Name schon sagt, machen wir hier einen kompletten Abzug des Wurzelverzeichnisses oder des angegebenen Verzeichnisses.
- <Computer Path> = Hiermit geben wir das Zielverzeichnis auf dem Computer an. Der Inhalt des Isolated Storage wird in dieses Verzeichnis kopiert.
Unterhalb des angegebenen Verzeichnisses wird immer noch ein Ordner „IsolatedStore“ angelegt. Wenn dieser Ordner bereits existiert, wird der Inhalt überschrieben. Das selbe Kommando kann verwendet werden, um nur den Inhalt eines Unterverzeichnisses im Isolated Storage zu kopieren. Wie oben gibt man hier im Kommandozeilenbefehl das entsprechende Unterverzeichnis an. Wenn Ihre Anwendung eine Windows Phone 7.1 Anwendung ist, wird der Ordner „IsolatedStore“ ein Unterverzeichnis „Shared“ enthalten. Hier sind die anwendungsspezifischen Daten der Hintergrundaufgaben (Background Transfer) und ShellContent der Secondary Tiles abgelegt. Wenn Sie den Inhalt mit dem obigen Kommando auf den Desktop kopiert haben, würde das Ergebnis in etwa so aussehen:
Was, wenn wir Dateien oder Verzeichnisse vom Computer zurück in den Isolated Storage bekommen wollen? Das ist eine gute Möglichkeit zum Testen, da wir hiermit ausprobieren können, wie sich die Anwendung bei Änderungen der Dateien oder Verzeichnisse verhält. Hier ist das Kommando:
ISETool.exe rs xd 04ce6350-faf0-4977–8e5b-fb288fc127c0 „C:\Users\Sami\Desktop\IsolatedStore“
Neu ist nur ein kleiner Teil:
- rs = Restore Snapshot (Abzug wiederherstellen). Wie der Name schon sagt, werden hiermit die Dateien/Verzeichnisse im Isolated Storage mit dem Verzeichnisinhalt auf dem Computer ersetzt. Beachten Sie, dass wir für das Quellverzeichnis hier „IsolatedStore“ mit im Pfad angeben müssen.
Erstellen wir eine beliebige Textdatei im Verzeichnis „IsolatedStore“. Dies entspricht der Wurzel des Isolated Storage im Emulator bzw. Gerät:
Wenn wir jetzt das Restore Snapshot Kommando aufrufen, wird die Datei im entsprechenden Isolated Storage auftauchen. Wenn wir jetzt unsere Anwendung wieder ausführen, werden wir sehen, dass sie die neue Datei anzeigt und auch der Inhalt vorhanden ist:
Damit Sie noch mal genau wissen, was Sie zu erwarten haben, sind hier die Ausgaben aller unserer Kommandos:
Zusammenfassung
Das war’s! Egal, was für Dateien oder Verzeichnisse Sie im Isolated Storage ablegen — mit dem Isolated Storage Explorer können Sie sicherstellen, dass der Inhalt tatsächlich der ist, den Sie wollten.
Adios!
Um eine komplette Windows Phone Anwendung mit dem Beispielcode dieses Artikels zur Verwendung des Isolated Storage Explorer runterzuladen, klicken Sie auf den Download Code Button:
Morgen werden wir uns ein weiteres cooles Thema anschauen: Wir werden sehen, wie man Windows Azure verwenden kann, um eine Windows Phone Anwendung zu unterstützen.
Bis dahin!