Get XML from Server for Use on Windows Phone
- by psheriff
When working with mobile devices you always need to take into account bandwidth usage and power consumption. If you are constantly connecting to a server to retrieve data for an input screen, then you might think about moving some of that data down to the phone and cache the data on the phone. An example would be a static list of US State Codes that you are asking the user to select from. Since this is data that does not change very often, this is one set of data that would be great to cache on the phone. Since the Windows Phone does not have an embedded database, you can just use an XML string stored in Isolated Storage. Of course, then you need to figure out how to get data down to the phone. You can either ship it with the application, or connect and retrieve the data from your server one time and thereafter cache it and retrieve it from the cache.
In this blog post you will see how to create a WCF service to retrieve data from a Product table in a database and send that data as XML to the phone and store it in Isolated Storage. You will then read that data from Isolated Storage using LINQ to XML and display it in a ListBox.
Step 1: Create a Windows Phone Application
The first step is to create a Windows Phone application called WP_GetXmlFromDataSet (or whatever you want to call it). On the MainPage.xaml add the following XAML within the “ContentPanel” grid:
<StackPanel> <Button Name="btnGetXml" Content="Get XML" Click="btnGetXml_Click" /> <Button Name="btnRead" Content="Read XML" IsEnabled="False" Click="btnRead_Click" /> <ListBox Name="lstData" Height="430" ItemsSource="{Binding}" DisplayMemberPath="ProductName" /></StackPanel>
Now it is time to create the WCF Service Application that you will call to get the XML from a table in a SQL Server database.
Step 2: Create a WCF Service Application
Add a new project to your solution called WP_GetXmlFromDataSet.Services. Delete the IService1.* and Service1.* files and the App_Data folder, as you don’t generally need these items. Add a new WCF Service class called ProductService. In the IProductService class modify the void DoWork() method with the following code:
[OperationContract]string GetProductXml();
Open the code behind in the ProductService.svc and create the GetProductXml() method. This method (shown below) will connect up to a database and retrieve data from a Product table.
public string GetProductXml(){ string ret = string.Empty; string sql = string.Empty; SqlDataAdapter da; DataSet ds = new DataSet();
sql = "SELECT ProductId, ProductName,"; sql += " IntroductionDate, Price"; sql += " FROM Product"; da = new SqlDataAdapter(sql, ConfigurationManager.ConnectionStrings["Sandbox"].ConnectionString);
da.Fill(ds);
// Create Attribute based XML foreach (DataColumn col in ds.Tables[0].Columns) { col.ColumnMapping = MappingType.Attribute; }
ds.DataSetName = "Products"; ds.Tables[0].TableName = "Product"; ret = ds.GetXml();
return ret;}
After retrieving the data from the Product table using a DataSet, you will want to set each column’s ColumnMapping property to Attribute. Using attribute based XML will make the data transferred across the wire a little smaller. You then set the DataSetName property to the top-level element name you want to assign to the XML. You then set the TableName property on the DataTable to the name you want each element to be in your XML. The last thing you need to do is to call the GetXml() method on the DataSet object which will return an XML string of the data in your DataSet object. This is the value that you will return from the service call. The XML that is returned from the above call looks like the following:
<Products> <Product ProductId="1" ProductName="PDSA .NET Productivity Framework" IntroductionDate="9/3/2010" Price="5000" /> <Product ProductId="3" ProductName="Haystack Code Generator for .NET" IntroductionDate="7/1/2010" Price="599.00" /> ... ... ...
</Products>
The GetProductXml() method uses a connection string from the Web.Config file, so add a <connectionStrings> element to the Web.Config file in your WCF Service application. Modify the settings shown below as needed for your server and database name.
<connectionStrings> <add name="Sandbox" connectionString="Server=Localhost;Database=Sandbox; Integrated Security=Yes"/></connectionStrings>
The Product Table
You will need a Product table that you can read data from. I used the following structure for my product table. Add any data you want to this table after you create it in your database.
CREATE TABLE Product( ProductId int PRIMARY KEY IDENTITY(1,1) NOT NULL, ProductName varchar(50) NOT NULL, IntroductionDate datetime NULL, Price money NULL)
Step 3: Connect to WCF Service from Windows Phone Application
Back in your Windows Phone application you will now need to add a Service Reference to the WCF Service application you just created. Right-mouse click on the Windows Phone Project and choose Add Service Reference… from the context menu. Click on the Discover button. In the Namespace text box enter “ProductServiceRefrence”, then click the OK button. If you entered everything correctly, Visual Studio will generate some code that allows you to connect to your Product service.
On the MainPage.xaml designer window double click on the Get XML button to generate the Click event procedure for this button. In the Click event procedure make a call to a GetXmlFromServer() method. This method will also need a “Completed” event procedure to be written since all communication with a WCF Service from Windows Phone must be asynchronous. Write these two methods as follows:
private const string KEY_NAME = "ProductData";
private void GetXmlFromServer(){ ProductServiceClient client = new ProductServiceClient();
client.GetProductXmlCompleted += new EventHandler<GetProductXmlCompletedEventArgs> (client_GetProductXmlCompleted);
client.GetProductXmlAsync(); client.CloseAsync();}
void client_GetProductXmlCompleted(object sender, GetProductXmlCompletedEventArgs e){ // Store XML data in Isolated Storage IsolatedStorageSettings.ApplicationSettings[KEY_NAME] = e.Result;
btnRead.IsEnabled = true;}
As you can see, this is a fairly standard call to a WCF Service. In the Completed event you get the Result from the event argument, which is the XML, and store it into Isolated Storage using the IsolatedStorageSettings.ApplicationSettings class. Notice the constant that I added to specify the name of the key. You will use this constant later to read the data from Isolated Storage.
Step 4: Create a Product Class
Even though you stored XML data into Isolated Storage when you read that data out you will want to convert each element in the XML file into an actual Product object. This means that you need to create a Product class in your Windows Phone application. Add a Product class to your project that looks like the code below:
public class Product{ public string ProductName{ get; set; } public int ProductId{ get; set; } public DateTime IntroductionDate{ get; set; } public decimal Price{ get; set; }}
Step 5: Read Settings from Isolated Storage
Now that you have the XML data stored in Isolated Storage, it is time to use it. Go back to the MainPage.xaml design view and double click on the Read XML button to generate the Click event procedure. From the Click event procedure call a method named ReadProductXml().Create this method as shown below:
private void ReadProductXml(){ XElement xElem = null;
if (IsolatedStorageSettings.ApplicationSettings.Contains(KEY_NAME)) { xElem = XElement.Parse( IsolatedStorageSettings.ApplicationSettings[KEY_NAME].ToString());
// Create a list of Product objects var products = from prod in xElem.Descendants("Product") orderby prod.Attribute("ProductName").Value select new Product { ProductId = Convert.ToInt32(prod.Attribute("ProductId").Value), ProductName = prod.Attribute("ProductName").Value, IntroductionDate = Convert.ToDateTime(prod.Attribute("IntroductionDate").Value), Price = Convert.ToDecimal(prod.Attribute("Price").Value) };
lstData.DataContext = products; }}
The ReadProductXml() method checks to make sure that the key name that you saved your XML as exists in Isolated Storage prior to trying to open it. If the key name exists, then you retrieve the value as a string. Use the XElement’s Parse method to convert the XML string to a XElement object.
LINQ to XML is used to iterate over each element in the XElement object and create a new Product object from each attribute in your XML file. The LINQ to XML code also orders the XML data by the ProductName. After the LINQ to XML code runs you end up with an IEnumerable collection of Product objects in the variable named “products”. You assign this collection of product data to the DataContext of the ListBox you created in XAML. The DisplayMemberPath property of the ListBox is set to “ProductName” so it will now display the product name for each row in your products collection.
Summary
In this article you learned how to retrieve an XML string from a table in a database, return that string across a WCF Service and store it into Isolated Storage on your Windows Phone. You then used LINQ to XML to create a collection of Product objects from the data stored and display that data in a Windows Phone list box. This same technique can be used in Silverlight or WPF applications too.
NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "Get XML From Server for Use on Windows Phone" from the drop-down.
Good Luck with your Coding,Paul Sheriff
** SPECIAL OFFER FOR MY BLOG READERS **Visit http://www.pdsa.com/Event/Blog for a free video on Silverlight entitled Silverlight XAML for the Complete Novice - Part 1.