Windows Phone 7 : How to Store Data and Pass Data between Pages

This is a walk-through tutorial for demonstrating following functions:

  • Store data on Windows Phone 7 by using Isolated Storage
  • Pass data through parameter between pages
  • Dynamic generate data grid, which contains data and related link button
  • Use XElement, XAttribute as data structure

I will explain related concepts through codes step by step. This application is used as a shopping list. Users will be able to key-in items they want to shop and view the shopping list as well. Add and Open function are implemented and I will leave remove function for you to try out.
Before start, You could download sourcecode here. And Windows Phone 7 developer package could be downloaded here.

1. Click on File -> New-> Project ... Let's name the project: ShoppingList_Demo.

2. Open Solution Explorer on the right side and right-click on References -> Add Reference... Here, select System.Xml. Linq. We will use XElement and XAttribute later, which is inside System.Xml.Linq package.

3. We will create a page for inserting shopping list records, so that we could retrieve records later. Right-Click on ShoppingList_Demo and select Add->New Item... Name the new page as AddItem.xaml.

4. Open AddItem.xaml, in TitlePanel, replace the ApplicationTitle as "My Shopping List" and replace Page Title as "New Item". Code is showed as below:

  <!--TitlePanel contains the name of the application and page title-->

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">

            <TextBlock x:Name="ApplicationTitle" Text="My Shopping List" Style="{StaticResource PhoneTextNormalStyle}"/>

            <TextBlock x:Name="PageTitle" Text="New Item" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>

        </StackPanel>

5. Inside ContentGrid panel, put in code showed as below:

  <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentGrid" Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" MinHeight="72.5"/>
                <RowDefinition Height="Auto" MinHeight="72.5"/>
                <RowDefinition Height="Auto" MinHeight="72.5"/>
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100*" />
                <ColumnDefinition Width="346*"/>
            </Grid.ColumnDefinitions>

            <TextBlock Grid.Column="0" Grid.Row="0" Text="Name:" HorizontalAlignment="Center" VerticalAlignment="Center" />

            <TextBox Name="nameTxt"  Grid.Column="1" Margin="8" Padding="2" Height="59"  />

            <TextBlock Grid.Column="0" Grid.Row="1" Text="Price:"  HorizontalAlignment="Center" VerticalAlignment="Center" />

            <TextBox x:Name="priceTxt"  Grid.Column="1" Margin="8" Padding="2" Height="59" Grid.Row="1"  />

            <TextBlock Grid.Column="0" Grid.Row="2" Text="Quantity:" HorizontalAlignment="Center" VerticalAlignment="Center" />

            <TextBox Name="quanTxt" Grid.Column="1" Margin="8" Padding="2" Height="59" Grid.Row="2" />

        </Grid>

        <Button x:Name="BtnSave" Content="Save" HorizontalAlignment="Right" Margin="0,0,17,0" Grid.Row="1" VerticalAlignment="Bottom" Click="BtnSave_Click" />

Basically, it helps you to populate a layout showed as below:

Let me explain the code here. Inside a grid, there are two types of definition. One is RowDefinition. As you can see here, we populate three rows with minheight 72.5 and the Grid will auto-fill the last row with proper height. The another one is ColumnDefinition. Here we specify them as 100* and 346*, which means the whole column width was devided by 100+346=446 units and the first column takes 100 units and the second column takes 346 units.
In each TextBlock, take the first one as a example. Grid.Row="0" and Grid.Column="0" indicate this TextBlock cell located in the first row and first column of the Grid. The rest components of the UI I believe are quite straight-forward to understand.

6. Choose Save button in the UI and double-click. It will bring you to the code-behind, which is BtnSave_Click method.

7. Add two namespaces that we will be using later.

 using System.IO.IsolatedStorage;
using System.Xml.Linq;

8. Insert the following code to BtnSave_Click method:

 using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
XDocument _doc = new XDocument();
XElement _item = new XElement(nameTxt.Text);
XAttribute price = new XAttribute("price", priceTxt.Text);
XAttribute quantity = new XAttribute("quantity", quanTxt.Text);
_item.Add(price, quantity);
_doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), _item);
IsolatedStorageFileStream location = new IsolatedStorageFileStream(nameTxt.Text + ".item", System.IO.FileMode.Create, storage);
System.IO.StreamWriter file = new System.IO.StreamWriter(location);
_doc.Save(file);
file.Dispose();
location.Dispose();
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
 XElement is a class that represents an XML element and the fundamental XML construct.  And XAttribute is used to create attribute for a specific XML element. You could check out details here. Here, we construct a XML as the following output:

<root price="xx" quantity="xx"></root>
 To store local data a on a Windows Phone 7 phone, the tool of choice is isolated storage. The reason to use "Isolated" is that all the data saved in one application can only be accessed within that application. You can check out more information here.
 using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())will allocate an instance for IsolatedStorageFile.
  Now, we will need to create a stream to write to. IsolatedStorageFileStream location = new IsolatedStorageFileStream(nameTxt.Text + ".item", System.IO.FileMode.Create, storage);

Here, a new stream is written to file named "xx.item".
After that, you dispose the temp file and location since everything is saved into phone storage. And we return to MainPage.xaml for content display.

9.Add in a navigation from MainPage.html to AddItem.xaml Page. Firstly, insert this code below:

 <phone:PhoneApplicationPage.ApplicationBar>

        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">

            <shell:ApplicationBar.MenuItems>

                <shell:ApplicationBarMenuItem Text="new" Click="New_Click"/>

            </shell:ApplicationBar.MenuItems>

        </shell:ApplicationBar>

    </phone:PhoneApplicationPage.ApplicationBar>

10. Add a method in MainPage.xaml.cs to navigate to Add New Item page.

 private void New_Click(object sender, EventArgs e)
{
NavigationService.Navigate(new Uri("/AddItem.xaml", UriKind.Relative));
}

Now Press Crtl+F5 and you will be able to see something like this while you click on ... in the interface.

11. We add a listBox in MainPage.xaml in order to display all the existing shopping lists. Add the coding below into <Grid x:Name="ContentGrid" ...>

 <!--ContentPanel - place additional content here-->

<Grid x:Name="ContentGrid" Grid.Row="1">
            <ListBox Grid.Row="0" Margin="10" FontSize="48" Name="Files">
            </ListBox>
</Grid>

12. Now we will retrieve whatever shopping list the user has saved. Insert the following code into MainPage constructor (public MainPage()) right after InitializeComponent().

 Loaded += (object sender, RoutedEventArgs e) =>
{
Files.Items.Clear();
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
foreach (string filename in storage.GetFileNames("*.item"))
{
    Grid a = new Grid();
    ColumnDefinition col = new ColumnDefinition();
    GridLength gl = new GridLength(200);
    col.Width = gl;
    a.ColumnDefinitions.Add(col);
    ColumnDefinition col2 = new ColumnDefinition();
    GridLength gl2 = new GridLength(200);
    col2.Width = gl;
    a.ColumnDefinitions.Add(col2);
    TextBlock txbx = new TextBlock();
    txbx.Text = filename;
    Grid.SetColumn(txbx, 0);
    HyperlinkButton btn = new HyperlinkButton();
    btn.Width = 100;
    btn.Content = "Show";
    btn.Name = filename;
    btn.NavigateUri = new Uri("/DisplayPage.xaml?item=" + filename, UriKind.Relative);
    Grid.SetColumn(btn, 1);
    a.Children.Add(txbx);
    a.Children.Add(btn);
    Files.Items.Add(a);
    }
    }
};

Here, we are trying to create a dynamic grid layout on fly with two columns. One is for displaying the file name and the other is to display a hyperlinkButton that tight to file name. We will build a DisplayPage.xaml for displaying details of the shopping item later on.

Noticed,

 btn.NavigateUri = new Uri("/DisplayPage.xaml?item=" + filename, UriKind.Relative);

Here, we use /DisplayPage.xaml ? item=.. to pass value of that specific item to the next page so that we know which shopping list's information we should display. This is very similar to how we pass parameters between web pages. Files here are the listBox functions as a container where content got displayed.

13. Now go back to MainPage.xaml. Press Ctrl+F5 and try to compile the file. Firstly, try to create some items if you don't have any. And after that, you should be able to see something like this:
You could truncate file name to display item name more friendly.

14. Now we will go ahead implement DisplayPage.xaml in order to display each individual item with details.  Right-Click on ShoppingList-Demo project choose Add->New Item... and choose Phone Portrait Page and name is DisplayPage.xaml.

15. Insert following code pieces into DisplayPage.xaml right after <!--TitlePanel contains the name of the application and page title-->

  <!--TitlePanel contains the name of the application and page title-->

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">

            <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>

            <TextBlock x:Name="PageTitle" Text="page name" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>

        </StackPanel>

        <!--ContentPanel - place additional content here-->

        <Grid x:Name="ContentGrid" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" Margin="10,10,10,77" ShowGridLines="True" Width="446" d:LayoutOverrides="GridBox">

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" MinHeight="72.5"/>

                <RowDefinition Height="Auto" MinHeight="72.5"/>

                <RowDefinition Height="Auto" MinHeight="72.5"/>

                <RowDefinition />

            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="0.3*" />

                <ColumnDefinition Width="*"/>

            </Grid.ColumnDefinitions>

            <TextBlock Grid.Column="0" Grid.Row="0" Text="Name:" HorizontalAlignment="Center" VerticalAlignment="Center" />

            <TextBlock Name="nameTxt"  Grid.Column="1" Margin="8" Padding="2" Height="59"  />

            <TextBlock Grid.Row="1" Text="Price:" HorizontalAlignment="Center" VerticalAlignment="Center" />

            <TextBlock x:Name="priceTxt"  Grid.Column="1" Margin="8" Padding="2" Height="59" Grid.Row="1"  />

            <TextBlock Grid.Column="0" Grid.Row="2" Text="Quantity:" HorizontalAlignment="Center" VerticalAlignment="Center" />

            <TextBlock Name="quanTxt" Grid.Column="1" Margin="8" Padding="2" Height="59" Grid.Row="2" />

        </Grid>

        <Button x:Name="BtnBack" Content="Home" HorizontalAlignment="Right" Margin="0,0,17,0" Grid.Row="1" VerticalAlignment="Bottom" Click="BtnBack_Click" />

This page's layout is almost identical to AddItem.xaml. Instead of having TextBox, we have all TextBlocks here to show information.

16. Insert these three namespaces into DisplayPage.xaml.cs file:

 using System.IO.IsolatedStorage;
using System.Xml.Linq;
using System.Windows.Navigation;

17. Insert the following code into DisplayPage.xaml.cs:

 protected override void OnNavigatedTo(NavigationEventArgs e)

        {
            String itemName = "";
            base.OnNavigatedTo(e);
            bool itemExists = NavigationContext.QueryString.TryGetValue("item", out itemName);
            if (itemExists)
            {
                PageTitle.Text = itemName;
            }
            using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
            {
                XElement _xml;
                IsolatedStorageFileStream location = new IsolatedStorageFileStream(itemName, System.IO.FileMode.Open, storage);
                System.IO.StreamReader file = new System.IO.StreamReader(location);
                _xml = XElement.Parse(file.ReadToEnd());   
               if (_xml.Name.LocalName != null)
                 {

                    XAttribute priceTemp = _xml.Attribute("price");
                    priceTxt.Text = priceTemp.Value.ToLower();
                    XAttribute quanTemp = _xml.Attribute("quantity");
                    quanTxt.Text = quanTemp.Value.ToLower();
                    nameTxt.Text = itemName;

                }
                file.Dispose();
                location.Dispose();
            }
        }

Here,

 NavigationContext.QueryString.TryGetValue("item", out itemName);

will return a boolean value to show whether this parameter's value has been passed from previous page. If it exists, we change the tile of this page to item name: PageTitle.Text = itemName.

Furthermore,

 IsolatedStorageFileStream location = new IsolatedStorageFileStream(itemName, System.IO.FileMode.Open, storage);

opens file "itemName" in local storage. You may want to improve the code to check whether the file is there or not. For demonstration purpose, I try to simplify the process.However, this is a line that may cause problem if the system couldn't find the file.

 

 System.IO.StreamReader file = new System.IO.StreamReader(location);_xml = XElement.Parse(file.ReadToEnd());

This is the code to parse file into XML elements and the following code is used to break down XML by elements and attributes.

 XAttribute priceTemp = _xml.Attribute("price");

Lastly, we dispose the file and location created temporarily.

18. Double-Click on the Home button in DisplayPage.xaml and a BtnBack_Click method will be auto-generated in DisplayPage.xaml. Insert the following code in to navigate this page:

 private void BtnBack_Click(object sender, RoutedEventArgs e)
        {
            NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
        }

19. Now Press Ctrl+F5 and you will be able to navigate between different pages with data stored and retrieved. Happy Shopping! :)