W artykule tym postaram się możliwie krótko i zrozumiale opisać zagadnienie izolowanej przestrzeni pamięci dla aplikacji, czyli Isolated Storage.
Na co mi to?
Miejsce to, odnosi się do kawałka dysku na komputerze po stronie klienta, w którym aplikacja ma prawo przetrzymywać swoje pliki i informacje. Do użytku mamy z defaultu 1MB ale wielkość pamięci może być zmieniona przez usera (o tym za chwilę).
Taka ilość miejsca oraz wyjątkowa łatwość dostępu, daje nam duże możliwości. Wielokrotnie wykorzystywałem tą przestrzeń, jako miejsc do przechowywania informacji o ustawieniach użytkownika, jako lokalny high scores dla gry lub też informacje dla ankiety, o tym czy użytkownik już dziś głosował. Zastosowań jest naprawdę wiele i warto pamiętać o tym rozwiązaniu.
Zróbmy sobie przykład
Jako przykład aplikacji zróbmy prosty program, w którym podajemy swoje imię, a program będzie je pamiętał i wyświetlał przy każdym kolejnym uruchomieniu.
Zacznijmy od dodania kontrolek do wprowadzenia danych
<TextBox Height="32" x:Name="EnterName" Width="192" Text="Enter your name here" TextWrapping="Wrap"/><Button Height="28" x:Name="SubmitButton" Width="106" Foreground="#FF313131" Content="Submit" Click="SubmitButton_Click">
oraz TextBlocka do wyświetlania zapamiętanego imienia
<TextBlock Height="91" x:Name="NameTextBlock" Width="284" TextWrapping="Wrap"><Run x:Name="UserName" FontFamily="Comic Sans MS" FontSize="36" FontWeight="Bold" Text="" />
</TextBlock>
Następnie w kodzie wywołujemy metodę do odczytu imienia oraz zapisu. Aby odczytać coś z przestrzeni aplikacji, należy stworzyć obiekt IsolatedStorageFile
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
który przyda nam się do sprawdzenia czy plik istnieje
if (!isf.FileExists("nasz_zapisany_plik.txt"))
oraz do utworzenia FileStream-era
IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("nasz_zapisany_plik.txt", FileMode.Open, isf);Teraz wystarczy podłączyć np. StreamReader i możemy robić odczyty jak na zwykłym pliku txt w aplikacji desktopowej.
StreamReader sr = new StreamReader(isfs);userName = sr.ReadToEnd();
sr.Close();
Aby zapisać coś w Isolated Storage, otwieramy plik z innym FileMode, np.
IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("nasz_zapisany_plik.txt", FileMode.Create, isf);
oraz zamiast StreamReadera, podłączamy StreamWriter
StreamWriter sw = new StreamWriter(isfs);Cała reszta jest analogiczna do operacji na zwykłych plikach.
Kompletna metoda zapisu:
private void SaveUserName(string name){
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(FILE_NAME, FileMode.Create, isf);StreamWriter sw = new StreamWriter(isfs);
try{
sw.WriteLine(String.Format("{0}", name));
}
catch (Exception ee) { }
finally
{
sw.Close();
isfs.Close();
isfs.Dispose();
isf.Dispose();
sw.Dispose();
}
}
Kompletna metoda odczytu:
private string ReadNameIfExist(){
string userName = String.Empty;
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
if (!isf.FileExists(FILE_NAME))
return userName;
IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(FILE_NAME, FileMode.Open, isf);
try
{
StreamReader sr = new StreamReader(isfs);
userName = sr.ReadToEnd();
sr.Close();
}
catch (Exception e) { }
finally
{
isf.Dispose();
}
return userName;
}
W powyższym "przykładzie", nie umieszczałem całego kodu z przygotowanego dema, ponieważ zrobił się trochę za długi (szczególnie generowany xaml), dlatego też po całość zapraszam do części source code.
Gotowa aplikacja wygląda tak: link. (Nie zapomnijcie o odświeżaniu strony => F5).
Tak jak Kuba słusznie zauważył (Kuba Florczyk), w swoim artykule pominąłem kwestie klasy IsolatedStorageSettings. IsolatedStorageSettings jest to klasa która zapewnie łatwiejszą obsługę Isolated Storage. Nie musimy deklarować IsolatedStorageFile i IsolatedStorageFileStream, wszystko mamy opakowane w klasie, a wszelkie operacji przeprowadzamy jak na Dictionary, czy też ASP-owej sesji (klucz-wartość).Można łatwiej? [edit 29-08-2008]
A więc podajemy klucz i przypisujemy wartość. Aby odczytać wartość, wystarczy podać klucz i otrzymujemy obiekt z zapisaną wcześniej wartością.Przykład użycia
Deklaracjaprivate IsolatedStorageSettings isolatedSettings = IsolatedStorageSettings.ApplicationSettings;
Zapis danychtry
Odczyt danych
{
isolatedSettings.Add("imie", name);
}
catch (ArgumentException ex)
{
// message z bledem
}string name = String.Empty;
Aktualizacja danych
try
{
name = (string)isolatedSettings["name"];
}
catch (System.Collections.Generic.KeyNotFoundException)
{
// blad i wartosc domyslna dla name
name = "anonymous";
}
isolatedSettings["name"] = "nowe imie";
Usuwanie danych
isolatedSettings.Remove("name");
Czyszczenie wszystkich ustawień
isolatedSettings.Clear();
Liczba ustawień
isolatedSettings.Count();
Lista dostępnych kluczy
isolatedSettings.Keys;
Lista dostępnych wartości
isolatedSettings.Values;
Jak widać, IsolatedStorageSettings zapewnia prace jak ze zwykłym słownikiem. Rozwiązanie to zaoszczędzi nam dużo czasu i uprości kod.
I na końcu, jeszcze raz dzięki Kubie za pomocny komentarz!!! :)
Jak zwiększyć wielkość dostępnej pamięci?
Otóż zwiększanie potrzebnej pamięci jest wyjątkowo proste ale musi zgodzić się na to użytkownik. W związku z tym, musimy go zapytać i jeśli się zgodzi, wtedy możemy zapisywać na jego dysk więcej danych. Limit pamięci do jakiej możemy zwiększyć jest ograniczony tylko przez wielkość typu ""long", a więc mniej więcej 8,589,934,592Gb :)A więc wystarczy wywołać kod:
private void SubmitButton_Click(object sender, RoutedEventArgs e)
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
Int64 sizeNeeded = 1024 * 1024 * 100;
if (sizeNeeded > isf.AvailableFreeSpace)
{
Int64 newValue = isf.Quota - isf.AvailableFreeSpace + sizeNeeded;
if (isf.IncreaseQuotaTo(newValue))
{
// np. zapis danych
}
}
}
}
Efekt:
User zobaczy ładne zapytanie i po jego akceptacji możemy zapisywać dane. Niestety, nie znalazłem możliwości modyfikacji treści zapytania, a więc informacja w okienku musi zostać taka jak jest. Dodatkowo, zapytanie może być wywołane tylko z eventów, a więc nie odpytamy użytkownika o zwiększenie ilości pamięci podczas inicjalizacji aplikacji. Możemy to zrobić dopiero gdy wciśnie jakiś przycisk w aplikacji (kwestie bezpieczeństwa).
uwaga bug:
Ponieważ zapytanie jest zawsze w języku angielskim, to w przypadku gdy użytkownik nie posiada systemu operacyjnego w tym języku, zapytanie o zwiększenie ilości danych nigdy się nie pojawi i zawsze będzie zwracać "false". Bug jest zgłoszony do MS i można poczytać o nim więcej tutaj: http://silverlight.net/forums/p/51485/135114.aspx
Gdzie to jest?
Aby znaleźć dokładne miejsce przetrzymywania informacji, najłatwiej jest użyć debugera.

Zmienna "m_AppFilePath" podpowie nam, że jest to gdzieś w przestrzeni naszego użytkownika oraz w przestrzeni assembly naszej aplikacji. Taka ścieżka nie jest łatwa do zapamiętania i może wyglądać np. tak:
"C:\\Users\\ToJaUser\\AppData\\LocalLow\\Microsoft\\Silverlight\\is\\3tumcpvn.hf1\\wrelfaen.uut\\1\\s\\rtqyd4tgtuurqnzlqaf3otinrvcefdf4aeqlkxhw0dltde2t2uaaaeaa\\f"
Jak to usunąć?
Aby zobaczyć ile miejsca zajmuje aplikacja i ewentualnie wyczyścić wgrane pliki, wystarczy że klikniemy prawym na aplikacji Silverlight i wybierzemy "Silverlight Configuration"
Następnie przejdziemy do zakładki "Application Storage".

W zakładce tej mamy komplet informacji o aplikacjach, łącznie z ilością miejsca jakie zajmują na naszych dyskach.
Source code
Working demo
Reources
- MSDN System.IO.IsolatedStorage Namespace - link
- Blog trzech panów: "Switch On The Code" link – trochę nieaktualne, ale to tu znalazłem kiedyś pierwsze wzmianki o Isolated Storage Memory
Pozdrawiam,
Jacek Ciereszko










