📜 ⬆️ ⬇️

Create an application for Windows Phone 7 from start to finish. Part 12. Isolated storage: saving and loading data

Previous part

As already mentioned in the previous section, isolated storage is usually used on the phone for long-term storage.

In this part you will learn:

Using isolated storage


Isolated storage is the analogy of a file system on a desktop computer. However, each application can only access its part of isolated storage and does not have access to data used by any other application.
')
Tip:
Even if you save and retrieve data from outside using web services, you must cache the data on the phone for future use if the network is not available, and in this case you will need to use isolated storage for this purpose.

Isolated storage is slower than in-memory dictionaries, so you should only use it for data that potentially requires long-term storage and for non-serializable objects that are not supported by state dictionaries. Most classes are serializable by default, but some are not and, accordingly, require special handling, as will be described below. For example, a non-serializable is an ImageSource object, which is a binary image file, such as a JPG.

You should carefully consider where your code is located to work with isolated storage in order to minimize the impact of any delays. For example, if your application has a lot of data to store, you should avoid saving it at the same time when you exit the application, and instead save it gradually. In general, you should save data to isolated storage as soon as it becomes available or as far in advance as possible.

Tip:
Unlike the desktop version of Silverlight, there is no limit on the phone’s amount of isolated storage space that an application can use. However, phones have a significantly smaller total amount of storage space compared to desktop computers, so you should use as little space as possible.

Isolated storage provides two forms of access:
Isolated repository state dictionary is easy to use. You simply access it through the static property IsolatedStorageSettings.ApplicationSettings, and then add, delete, or change values, as you would with a regular dictionary. The dictionary automatically performs the work of loading and storing values ​​to isolated storage at a suitable time, although you can also override this behavior.

To use isolated storage to save and load files, use the IsolatedStorageFile class, which is a virtual file system interface. You can access the repository by calling the GetUserStoreForApplication method. Once you have accessed the repository, you can call methods to open, create, and delete files and folders.

Saving data in isolated storage


In order to save the object being serialized to isolated storage using a dictionary, you simply assign the IsolatedStorageSettings.ApplicationSettings object and specify the key. Then you call the IsolatedStorageSettings.Save method. Calling Save is not required, because the application will automatically save all stored values ​​when exiting the application.

The following code snippet shows how to save the Car object (without taking into account its non-serializable Picture property) in the dictionary. The CarDataStore.SaveCar method assigns the value of the Car property directly to the IsolatedStorageSettings.ApplicationSettings dictionary using the CAR_KEY constant. The value will be written to the isolated storage when the IsolatedStorageSettings.Save method is called in the next line.
private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  1. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  2. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  3. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  4. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  5. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  6. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  7. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  8. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  9. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  10. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  11. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  12. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  13. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  14. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  15. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  16. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  17. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
  18. private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .
private const string CAR_PHOTO_FILE_NAME = "CarPhoto.jpg" ; private const string CAR_KEY = "FuelTracker.Car" ; private static readonly IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings; public static void SaveCar(Action errorCallback) { try { appSettings[CAR_KEY] = Car; appSettings.Save(); SaveCarPhoto(CAR_PHOTO_FILE_NAME, Car.Picture, errorCallback); DeleteTempCarPhoto(); } catch (IsolatedStorageException) { errorCallback(); } } * This source code was highlighted with Source Code Highlighter .

It is possible that there is no available space in the isolated storage. In this case, a call to the Save method will throw an exception. It is important to handle the exception, but it does not make sense to do it at the data access level (in the data access layer). Instead, a warning is usually displayed for the user. For this reason, the SaveCar method is accepted by the Action delegate, which is called when an exception occurs. This allows the calling code to handle errors in its own way, without the need for its own Try / Catch block or any dependency on a specific type of exception. This method will be described in more detail in the “Validation of Input Data” section.

Certification Requirement:
Your application should handle all exceptions and not terminate unexpectedly.

The Car.Picture property is a car image and is an object of type BitmapImage. Since the BitmapImage type is not serializable, the Car.Picture property cannot be stored in the IsolatedStorageSettings.ApplicationSettings dictionary, so the image will be considered separately. However, you must explicitly exclude the Picture property from serialization or a call to the Save method will always throw an exception. To exclude a property from serialization, you must apply IgnoreDataMemberAttribute to it, as shown in the following example from Car.cs.
  1. [System.Runtime.Serialization.IgnoreDataMemberAttribute]
  2. public BitmapImage Picture
  3. {
  4. get { return _picture; }
  5. set
  6. {
  7. _picture = value ;
  8. NotifyPropertyChanged ( "Picture" );
  9. }
  10. }
* This source code was highlighted with Source Code Highlighter .

To save the file to isolated storage, use the IsolatedStorageFile.CreateFile or IsolatedStorageFile.OpenFile methods. There are also methods in IsolatedStorageFile to check if the file exists and to create directories.

Since the Car.Picture property is not serializable, it is saved as a file in isolated storage. It would be great if you could just refer to the selected photo by reference in the media library so that you do not need to save it separately in an isolated storage. However, at the time of writing this article it was not possible. Additional information will be presented in the section “Accessing photos on Windows Phone”.

The following code shows the SaveCarPhoto method, which saves the image of the car as a file in a directory in isolated storage. The OpenFile method is used to create a new file. Note the use of the Extensions.SaveJpeg method. This helper method simplifies the conversion of BitmapImage to Stream.
  1. private const string CAR_PHOTO_DIR_NAME = "FuelTracker" ;
  2. private static void SaveCarPhoto ( string fileName, BitmapImage carPicture,
  3. Action errorCallback)
  4. {
  5. if (carPicture == null ) return ;
  6. try
  7. {
  8. using ( var store = IsolatedStorageFile.GetUserStoreForApplication ())
  9. {
  10. var bitmap = new WriteableBitmap (carPicture);
  11. var path = Path.Combine (CAR_PHOTO_DIR_NAME, fileName);
  12. if (! store.DirectoryExists (CAR_PHOTO_DIR_NAME))
  13. {
  14. store.CreateDirectory (CAR_PHOTO_DIR_NAME);
  15. }
  16. using ( var stream = store.OpenFile (path, FileMode .Create))
  17. {
  18. Extensions.SaveJpeg (bitmap, stream,
  19. bitmap.PixelWidth, bitmap.PixelHeight, 0, 100);
  20. }
  21. }
  22. }
  23. catch (IsolatedStorageException)
  24. {
  25. errorCallback ();
  26. }
  27. }
* This source code was highlighted with Source Code Highlighter .

The following image shows how a Car object is stored in isolated storage.

image

Reading data from isolated storage


To read an object from a dictionary in isolated storage, you simply specify the key of the object you want. The following code demonstrates how to retrieve values ​​from a dictionary in isolated storage. This is the code for the CarDataStore.Car property from the "Using Access to Class Data" section, but this time it includes details regarding data storage.
  1. private static Car car;
  2. public static Car Car
  3. {
  4. get
  5. {
  6. if (car == null )
  7. {
  8. if (appSettings.Contains (CAR_KEY))
  9. {
  10. car = (Car) appSettings [CAR_KEY];
  11. car.Picture = GetCarPhoto (CAR_PHOTO_FILE_NAME);
  12. }
  13. else
  14. {
  15. car = new car ()
  16. {
  17. FillupHistory = new ObservableCollection <Fillup> ()
  18. };
  19. }
  20. }
  21. return car;
  22. }
  23. set {car = value ; }
  24. }
* This source code was highlighted with Source Code Highlighter .

In this code, if the Car property does not yet have a value, it is checked whether the dictionary contains a value for the key specified by the CAR_KEY constant. (Using a constant allows you to select a value using IntelliSense, which avoids the possibility of a typo.) If there is no existing value, a new instance of the Car object and its FillupHistory property are initialized . Otherwise, Car is taken from the dictionary. However, it should be noted that the property Car.Picture is of type BitmapImage, which is not serializable. Since the IsolatedStorageSettings dictionary only supports serializable values, the Picture property must be processed separately.

To read files from isolated storage, use the IsolatedStorageFile.OpenFile method. The following code shows the GetCarPhoto method in the Fuel Tracker application, retrieving an image from isolated storage.
  1. private static BitmapImage GetCarPhoto ( string fileName)
  2. {
  3. using (IsolatedStorageFile store =
  4. IsolatedStorageFile.GetUserStoreForApplication ())
  5. {
  6. string path = Path.Combine (CAR_PHOTO_DIR_NAME, fileName);
  7. if (! store.FileExists (path)) return null ;
  8. IsolatedStorageFileStream stream =
  9. store.OpenFile (path, FileMode .Open);
  10. try
  11. {
  12. var image = new BitmapImage ();
  13. image.SetSource (stream);
  14. return image;
  15. }
  16. finally
  17. {
  18. stream.Dispose ();
  19. }
  20. }
  21. }
* This source code was highlighted with Source Code Highlighter .

Remove data from isolated storage


To remove values ​​from the state dictionary, call the IsolatedStorageSettings.Remove method and specify the key. Then call the IsolatedStorageSettings.Save method to apply the changes. To remove a file from isolated storage, call the IsolatedStorageFile.DeleteFile method.

The following CarDataStore.DeleteCar method demonstrates how to delete a value from the state dictionary and delete the file.
  1. public static void DeleteCar ()
  2. {
  3. Car = null ;
  4. appSettings.Remove (CAR_KEY);
  5. appSettings.Save ();
  6. DeleteCarPhoto ();
  7. DeleteTempCarPhoto ();
  8. }
* This source code was highlighted with Source Code Highlighter .

The CarDataStore.DeleteCar method first sets the Car property to null so that it can be reinitialized if it is accessed in the future. Then the CAR_KEY entry is removed from the ApplicationSettings dictionary, and the Save method is called to apply the changes. Finally, this method removes photographs of the car from isolated storage.

Interface Design Recommendation:
Application actions that overwrite or delete data or cannot be reversible should have a Cancel button.

Next part

Source: https://habr.com/ru/post/118455/


All Articles