Mike Snows Silverlight Blog

Game Programming with Silverlight 2.0
Tip of the Day #20 – How to Increase the Isolated Storage Quota for your App.

Each application by default is given 1 MB of storage space through Isolated Storage where the server is able to store client specific data on the clients machine. So what if you need more than 1 MB? Fortunately, the IsolatedStorageFile object provides a method called IncreaseQuotaTo() that allows a server to prompt the user for permission to increase the amount of storage.

You can see the list of applications that are using IsolatedStorage on your box. To do this:

  1. Right click on any Silverlight Application and from the context menu choose the only option: “Silverlight Configuration”.

SC

  1. This will bring up the Silverlight Configuration Dialog: 

    1
  2. Click on the last tab “Application Storage” and you will see a list of all web sites that are using Isolated Storage on this machine. The current amount of space each app is using as well as the quota allowed per app are also listed. 

    2

For our demo we will be displaying 3 pieces of information:

  1. Current Spaced Used
  2. Current Space Available
  3. Current Quota

I have added a text box where you can specify the number of bytes you wish to increase your quota by. By clicking on the button, this will then make the necessary call to IncreaseQuotaTo() in order to increase the quota.

***Note: You must call this function from a user event such as a button click. For security reasons, calling IncreaseQuotaTo() directly will automatically return false since the user did not instantiate the call.

Below is a screen shot of the test application we are creating:

3

When you enter a value and click on the button you will be prompted with this dialog:

4

If you click yes, you will see the new results:

image

Now, let’s look at our XAML code in Page.xaml that we use to set up this interface:

<UserControl x:Class="IncreaseIsolatedStorage.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="800" Height="600">
<Grid x:Name="LayoutRoot" Background="White">
    <Canvas Canvas.Left="10" Canvas.Top="10">
        <TextBlock Canvas.Left="10" x:Name="SpacedUsed" >Current Spaced Used=</TextBlock>
        <TextBlock Canvas.Left="10" x:Name="SpaceAvaiable" Canvas.Top="20">Current Space Available=</TextBlock>
        <TextBlock Canvas.Left="10" x:Name="CurrentQuota" Canvas.Top="40">Current Quota=</TextBlock>
        <TextBlock Canvas.Left="10" x:Name="NewSpace" Canvas.Top="70">New space (in bytes) to request=</TextBlock>
        <TextBox Canvas.Left="255" Canvas.Top="70" Width="100" x:Name="SpaceRequest"></TextBox>
        <TextBlock Canvas.Left="365" Canvas.Top="70" Width="60">(1048576 = 1 MB)</TextBlock>
        <Button Canvas.Left="10" Content="Increase Storage" Canvas.Top="100" 
                Width="100" Height="50" Click="Button_Click">                
        </Button>
        <TextBlock Canvas.Left="10" Canvas.Top="160" x:Name="Results"></TextBlock>
    </Canvas>
</Grid>
</UserControl>

And our code behind:

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.Shapes;
using System.IO.IsolatedStorage;
 
namespace SilverlightApplication11
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
 
            SetStorageData();
        }
 
        private void SetStorageData()
        {
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                SpacedUsed.Text     = "Current Spaced Used = "+(isf.Quota - isf.AvailableFreeSpace).ToString() +" bytes";
                SpaceAvaiable.Text  = "Current Space Available=" + isf.AvailableFreeSpace.ToString() + " bytes";
                CurrentQuota.Text   = "Current Quota=" + isf.Quota.ToString() + " bytes";
            }
        }
        
        private void IncreaseStorage(long spaceRequest)
        {
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                long newSpace = isf.Quota + spaceRequest; 
                try
                {
                    if (true == isf.IncreaseQuotaTo(newSpace))
                    {
                        Results.Text = "Quota successfully increased.";
                    }
                    else
                    {
                        Results.Text = "Quota increase was unsuccessfull.";
                    }
                }
                catch (Exception e)
                {
                    Results.Text = "An error occured: "+e.Message;
                }
                SetStorageData();
            }
        }
 
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                long spaceRequest = Convert.ToInt64(SpaceRequest.Text);
                IncreaseStorage(spaceRequest);
            }
            catch { // User put bad data in text box }
        }
    }
}

Complete Source code: IsolatedStorageIncrease

Demo:

For a complete demo of this tutorial click here.

Tip of the Day #19: Using Isolated Storage

Silverlight uses Isolated Storage as a virtual file system to store data in a hidden folder on your machine. It breaks up the data into two separate sections: Section #1 contains administrative information such as disk quota and section #2 contains the actual data. Each Silverlight application is allocated its own portion of the storage with the current quota set to be 1 MB per application.

Advantages:

  1. Isolated Storage is a great alterative to using cookies (as discussed in Tip of the Day #18) especially if you are working with large sets of data. Examples of use include undo functionality for your app, shopping cart items, window settings and any other setting your application can call up the next time it loads.
  2. Isolated storage stores by user allowing server applications to dedicate unique settings per individual user.

Possible Pitfalls:

  1. Administrators can set disk quota per user and assembly which means there is no guarantee on space available. For this reason, it is important to add exception handling to your code.
  2. Even though Isolated Storage is placed in a hidden folder it is possible, with a bit of effort, to find the folder. Therefore the data stored is not completely secure as users can change or remove files. It should be noted though that you can use the cryptography classes to the encrypt data stored in isolated storage preventing users from changing it.
  3. Machines can be locked down by administrative security policies preventing applications from writing to the IsolatedStorage. More specifically, code must have the IsolatedStorageFilePermission to work with isolated storage.

All that said, let’s take a look at how we save and load data. Note that you will need to add a using statement to reference the namespace System.IO.IsolatedStorage as well as System.IO.

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.Shapes;
using System.IO.IsolatedStorage;
using System.IO;
 
namespace SilverlightApplication10
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            SaveData("Hello There", "MyData.txt");
            string test = LoadData("MyData.txt");
        }
 
        private void SaveData(string data, string fileName)
        {
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Create, isf))
                {
                    using (StreamWriter sw = new StreamWriter(isfs))
                    {
                        sw.Write(data);
                        sw.Close();
                    }
                }
            }
        }
 
        private string LoadData(string fileName)
        {
            string data = String.Empty;
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Open, isf))
                {
                    using (StreamReader sr = new StreamReader(isfs))
                    {
                        string lineOfData = String.Empty;
                        while ((lineOfData = sr.ReadLine()) != null)
                            data += lineOfData;
                    }
                }
            }
            return data;
        }
    }
}
Tip of the Day #18: How to Set Browser Cookies.

Cookies are strings of text that the server can store on the client side. These cookies can then be sent back by the client to the server each time the client accesses that server again. Cookies are commonly used for session tracking, authentication, site preferences and maintaining specific information about users.  For example, items a client stores in a shopping cart can be stored on the client side as cookies so that they can leave the online store and return later to check out.

So, how do we set cookies from within a Silverlight application? To accomplish this we turn again to the HtmlPage.Document object. To use this object you must add a using statement to reference the System.Windows.Browser namespace.

To set a cookie we need to call SetProperty() with a string in the following format: “Key=Value;expires=ExpireDate.”

For example:

private void SetCookie(string key, string value)
{
    // Expire in 7 days
    DateTime expireDate = DateTime.Now + TimeSpan.FromDays(7);
 
    string newCookie = key + "=" + value + ";expires=" + expireDate.ToString("R");
    HtmlPage.Document.SetProperty("cookie", newCookie);
}

Now, to get the cookie we split up and iterate through all the cookies returned through the property HtmlPage.Document.Cookies.

For example:

private string GetCookie(string key)
{
    string[] cookies = HtmlPage.Document.Cookies.Split(';');
 
    foreach (string cookie in cookies)
    {
        string [] keyValue = cookie.Split('=');
        if (keyValue.Length == 2)
        {
            if(keyValue[0].ToString() == key)
                return keyValue[1];
        }
    }
    return null;
}

For more details on additional properties you can set when creating cookies please see MSDN.

Tip of the Day #17: How to Animate a Rotating Image

Each Silverlight element exposes a property called RenderTransform that is used to set the transform information that affects the rendering position of the element. I will be demo’ing a non-stop circular transform rotation of an image in a Storyboard timer.

First, let’s declare the the image in our Page.xaml. Make certain to add the image your are setting the source to here to your project in VS. Since we are rotating around the center of the image, we set the CenterX and CetnerY to be the center coordinates of the image which. In my case the image I am using is 64x48 pixels so the center is set at CenterX=32, CenterY=24.

Page.XAML:

<Image x:Name="FireballLogo" Source="images/Fireballlogo.png">
    <Image.RenderTransform>
        <RotateTransform x:Name="FireballTransform" CenterX="32" CenterY="24">                        
        </RotateTransform>
    </Image.RenderTransform>
</Image>

Next, let’s setup our game loop timer using the Storyboard discussed in Tip of the Day #16.

Page.XAML.cs class constructor:

Storyboard _gameLoop = new Storyboard();
public Page()
{
    InitializeComponent();
 
    _gameLoop.Duration = TimeSpan.FromMilliseconds(0);
    _gameLoop.Completed += new EventHandler(MainGameLoop);
    _gameLoop.Begin();
}

And, finally in our MainGameLoop we simply increment the angle by 1 and call Transform on the element to get it to rotate:

void MainGameLoop(object sender, EventArgs e)
{
    FireballTransform.Angle += 1;
    FireballTransform.Transform(new Point(32, 24));
    _gameLoop.Begin();
}
Tip of the Day #16 - StoryBoard versus DispatcherTimer for Animation and Game Loops.

When it comes to animation and game loops, it seems the overall consent is that using an empty StoryBoard is currently the best, most efficient way to go.

From my research, the reasons why are as follows:

  1. The StoryBoard is handled on a separate thread that is not affected by the UI thread which the DispatcherTimer is on.
  2. The DispatcherTimer is a lower resolution timer than the timer behind the Storyboard class, which causes loss in fidelity.
  3. The Storyboard execution is more stable across the different supported OS’s and web browsers.

Given that, let’s take a look at how it can be done. In the example below, we create a StoryBoard timer and increment and display a count variable that represents the number of times the MainGameLoop was called. I have set my duration between calls to be zero milliseconds but you will want to change this to whatever best fits your animation story.

Page.xaml.cs:

namespace SilverlightApplication8
{
    public partial class Page : UserControl
    {
        Storyboard _gameLoop = new Storyboard();
        int count = 0;
 
        public Page()
        {
            InitializeComponent();
            _gameLoop.Duration = TimeSpan.FromMilliseconds(0);
            _gameLoop.Completed += new EventHandler(MainGameLoop);
            _gameLoop.Begin();
        }
 
        void MainGameLoop(object sender, EventArgs e)
        {
            // Add any game logic/animation here.
            // Example:
            myTextbox.Text = count.ToString();
            count++;
 
            // Continue storyboard timer
            _gameLoop.Begin();
        }
    }
}
Page.xaml:
<UserControl x:Class="SilverlightApplication8.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock x:Name="myTextbox">Display Counter</TextBlock>
    </Grid>
</UserControl>
Tip of the Day #15 – Communicating between JavaScript & Silverlight

Communicating between Javascript and Silverlight is, fortunately, relatively straight forward. The following sample demonstrates how to make the call both ways.

Calling Silverlight From Java script:

  1. In the constructor of your Silverlight app, make a call to RegisterScriptableObject().This call essentially registers a managed object for scriptable access by JavaScript code. The first parameter is any key name you want to give. This key i