Communicating Windows Phone 7.1 with WebAPI to perform Create and Read Operations

Posted by: Mahesh Sabnis , on 7/17/2012, in Category Windows Phone
Views: 21860
Abstract: With the communication capability between WebAPI and Windows Phone, we can easily develop disconnected applications. In this article, we will explore how to use WebAPI on demand.

Last week, one of my clients, for whom I had conducted a WP7 training, called me. He wanted to know of a 'modern' way to expose and consume services over the Web. The business scenario was, the sales person who is carrying a WP device should record the sales information and then submit it to a Web service. I decided to create a demo application for this scenario using WebAPI. In this article, we will review the steps to build this application.

ASP.NET WebAPI provides a framework to implement web services over HTTP. It maps HTTP verbs like Get, Put, Post, Delete and others to respective service methods. To use WebAPI, in VS2010 we need to install ASP.NET MVC 4 RC or otherwise we get them in VS2012 RC by default installed. WebAPI services are implemented as controller Actions. WebAPI supports multiple content types. We will use the JavaScript Object Notation (JSON).

In the article, I am using VS2010 and Windows Phone 7.1 SDK.

For this application, I am using Sql Server 2008 R2 and the following tables:

I1_TB

Step 1: Open VS2010 and create a blank solution, name it as ‘Web_API_WP7’. In this solution, add a new ASP.NET Empty Web Site; name it as ‘Web_API_WP7’.

Step 2: In this ASP.NET application, add a class file. This will contain classes which map themselves to above tables and it will also contain classes for performing database operations.

namespace Web_API_WP7
{
public class StockiestMaster
{
    public int StockiestID { get; set; }
    public string StockiestName { get; set; }
}
public class MedicineMaster
{
    public int MedicineId { get; set; }
    public string MedicineName { get; set; }
    public int UnitePrice { get; set; }
}
public class QuoteMaster
{
    public int QuoteId { get; set; }
    public int MedicineId { get; set; }
    public int Quantity { get; set; }
    public int QuotePrice { get; set; }
}
public class StockiestBillMaster
{
    public int StockiestBillId { get; set; }
    public int StockiestID { get; set; }
    public int TotalBill { get; set; }
}

/// <summary>
/// Class for DB operations
/// </summary>
public class DataAccess
{
    SqlConnection Conn;
    SqlCommand Cmd;

    public DataAccess()
    {
        Conn = new SqlConnection("Data Source=.;Initial Catalog=Medical;Integrated Security=SSPI");
    }
    /// <summary>
    /// Method to Read all Stockiests
    /// </summary>
    /// <returns></returns>
    public StockiestMaster[] GetStockiest()
    {
        StockiestMaster[] Stockiests = null;
        Conn.Open();
        Cmd = new SqlCommand();
        Cmd.Connection = Conn;
        Cmd.CommandText = "Select * from StockiestMaster";
        var Reader = Cmd.ExecuteReader();
        DataTable Dt = new DataTable();
        Dt.Load(Reader);
        Stockiests = new  StockiestMaster[Dt.Rows.Count];
        int i = 0;
        foreach (DataRow Dr in Dt.Rows)
        {
            Stockiests[i] = new StockiestMaster()
            {
                StockiestID = Convert.ToInt32(Dr["StockiestID"]),
                StockiestName = Dr["StockiestName"].ToString()
            };
            i=i+1;
        }
        Conn.Close();
        return Stockiests;
    }
    /// <summary>
    /// Method to Read All Medicine
    /// </summary>
    /// <returns></returns>

    public MedicineMaster[] GetMedicines()
    {
        MedicineMaster[] Medicines = null;
        Conn.Open();
        Cmd = new SqlCommand();
        Cmd.Connection = Conn;
        Cmd.CommandText = "Select * from MedicineMaster";
        var Reader = Cmd.ExecuteReader();
        DataTable Dt = new DataTable();
        Dt.Load(Reader);
        Medicines = new  MedicineMaster[Dt.Rows.Count];
        int i = 0;
        foreach (DataRow Dr in Dt.Rows)
        {
            Medicines[i] = new MedicineMaster()
            {
                MedicineId = Convert.ToInt32(Dr["MedicineId"]),
                MedicineName = Dr["MedicineName"].ToString(),
                UnitePrice = Convert.ToInt32(Dr["UnitPrice"])
            };
            i = i + 1;
        }
        Conn.Close();
        return Medicines;
    }
    /// <summary>
    /// Method to Create Quote
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public int CreateQuote(QuoteMaster obj)
    {
        int Created = 0;
        Conn.Open();
        Cmd = new SqlCommand();
        Cmd.Connection = Conn;
        Cmd.CommandText = "Insert into QuoteMaster" +
            "Values (@MedicineId,@Quantity,@QuotePrice)";
        Cmd.Parameters.AddWithValue("@MedicineId",obj.MedicineId);
        Cmd.Parameters.AddWithValue("@Quantity",obj.Quantity);
        Cmd.Parameters.AddWithValue("@QuotePrice",obj.QuotePrice);
        Created = Cmd.ExecuteNonQuery();
        if (Created > 0)
        {
            Created = 1;
        }
        Conn.Close();
        return Created;
    }

    public int CreateBill(StockiestBillMaster obj)
    {
        int Created = 0;
        Conn.Open();
        Cmd = new SqlCommand();
        Cmd.Connection = Conn;
        Cmd.CommandText = "Insert into StockiestBillMaster " +
            "Values (@StockiestBillId,@StockiestId,@TotalBill)";
        Cmd.Parameters.AddWithValue("@StockiestBillId",obj.StockiestBillId);
        Cmd.Parameters.AddWithValue("@StockiestId",obj.StockiestID);
        Cmd.Parameters.AddWithValue("@TotalBill",obj.TotalBill);
        Created = Cmd.ExecuteNonQuery();
        if (Created > 0)
        {
            Created = 1;
        }
        Conn.Close();
        return Created;
    }

}
}

 Step 3: Add the following configuration in Web.Config file to support HTTP method modules:

<system.webServer>

    <modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

Step 4: Add a Global.asax file in the project. Add the route mapped in the Application_Start of the Global.asax which defines routing for the method call from client application:

RouteTable.Routes.MapHttpRoute(name: "DefaultApi",           
 routeTemplate: "api/{controller}/{id}",            
  defaults: new { id = System.Web.Http.RouteParameter.Optional });

Step 5: Right click on the project and add the controller class as shown below:

I2_Controller_Class

Add the following controller classes:

StockiestController.cs

using System.Collections.Generic;
using System.Web.Http;
 
namespace Web_API_WP7
{
    /// <summary>
    /// Controller class used to Read all Stockiest
    /// </summary>
    public class StockiestController : ApiController
    {
        DataAccess objContext;
 
        public StockiestController()
        {
            objContext = new DataAccess();
        }
        // GET api/<controller>
        public IEnumerable<StockiestMaster> Get()
        {
            var Stockiests = objContext.GetStockiest();
            return Stockiests;
        }
    }
}

 MedicineController.cs

using System.Collections.Generic;
using System.Web.Http;
 
namespace Web_API_WP7
{
    /// <summary>
    /// Class to Read all medicine
    /// </summary>
    public class MedicineController : ApiController
    {
        DataAccess objContext;
 
        public MedicineController()
        {
            objContext = new DataAccess();
        }
        // GET api/<controller>
        public IEnumerable<MedicineMaster> Get()
        {
            var Medicines = objContext.GetMedicines();
            return Medicines;
        }
    }
}


QuoteController.cs

 using System.Web.Http;
 
namespace Web_API_WP7
{
    /// <summary>
    /// Class to save Itesm wise Data
    /// </summary>
    public class QueoteController : ApiController
    {
        DataAccess objContext;
        public QueoteController()
        {
            objContext = new DataAccess();
        }
 
        // POST api/<controller>
        public void Post(QueoteMaster value)
        {
            objContext.CreateQueote(value);
        }
    }
}

BillController.cs

using System.Web.Http;
 
namespace Web_API_WP7
{
    /// <summary>
    /// Class to Save Bill in Table
    /// </summary>
    public class BillController : ApiController
    {
        DataAccess objContext;
        public BillController()
        {
            objContext = new DataAccess();
        }
 
 
        // POST api/<controller>
        public void Post(StockiestBillMaster value)
        {
            objContext.CreateBill(value);
        }
    }
}

Build the application and make sure that it is error free.

Creating Windows Phone 7.1 client

Step 1: In the solution created above, add a new Windows Phone project and name it as ‘WP7_WEB_API_Client’.

Step 2: Since this client will be making call to WebAPI, the communication will be performed using JSON message format. To support the JSON serialization, we need to add the following references in this client project:

  • System.Runtime.Serialization.
  • System.ServiceModel.Web

Step 3: To implement simple object based coding, add a class file in this project and name it as DataClasses.cs and add the following classes in it:

namespace WP7_WEB_API_Client
{
    public class StockiestMaster
    {
        public int StockiestID { get; set; }
        public string StockiestName { get; set; }
    }
    public class MedicineMaster
    {
        public int MedicineId { get; set; }
        public string MedicineName { get; set; }
        public int UnitePrice { get; set; }
    }
    public class QueoteMaster
    {
        public int QueoteId { get; set; }
        public int MedicineId { get; set; }
        public int Quantity { get; set; }
        public int QueotePrice { get; set; }
    }
    public class StockiestBillMaster
    {
        public int StockiestBillId { get; set; }
        public int StockiestID { get; set; }
        public int TotalBill { get; set; }
    }
}

Note, the above classes will perform encapsulation over the data received from the WebAPI using GET operations and will be used to display the data in the UI element. Also, for POST operations, these object will be serialized in JSON format.

Step 4: Design the MainPage.xaml as below:

I3_Design

The XAML will be as shown below:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
    <RowDefinition Height="149*" />
    <RowDefinition Height="500*" />
</Grid.RowDefinitions>
<TextBlock Height="30" HorizontalAlignment="Left" Margin="26,24,0,0"
Name="textBlock1" Text="Stockiest Name:" VerticalAlignment="Top" Width="155" />
<ListBox Height="100" HorizontalAlignment="Left"
    Margin="187,24,0,0" Name="lstStockiest" VerticalAlignment="Top"
    Width="251" BorderBrush="#006C1A1A" Background="#0000FFFF"
    ScrollViewer.VerticalScrollBarVisibility="Visible"
    BorderThickness="2" FontWeight="Bold" />
<StackPanel Grid.Row="1" Height="46" Margin="6,33,6,0" Name="stackPanel1"
    VerticalAlignment="Top" Orientation="Horizontal">
    <TextBlock Height="34" Name="textBlock2" Text="Medicine" Width="93" />
    <TextBlock Height="34" Name="textBlock3" Text="Unit Price" Width="93" />
    <TextBlock Height="34" Name="textBlock4" Text="Quantity" Width="93" />
    <TextBlock Height="34" Name="textBlock5" Text="Price" Width="93" />
</StackPanel>


<ListBox Grid.Row="1" Height="263" HorizontalAlignment="Left"
         Margin="6,79,0,0" Name="lstQueote" VerticalAlignment="Top"
         Width="444" ScrollViewer.VerticalScrollBarVisibility="Visible"/>
<Button Content="+" Grid.Row="1" Click="btnAddNew_Click" Height="73"
        HorizontalAlignment="Left" Margin="2,353,0,0" Name="btnAddNew"
        VerticalAlignment="Top" Width="66" />
<TextBox Grid.Row="1" Height="72" HorizontalAlignment="Left" Margin="282,360,0,0"
        Name="txttotalproce" Text="" VerticalAlignment="Top" Width="156"
        FontSize="30" FontWeight="Bold" />
<Button Content="Save Bill" Grid.Row="1" Height="62" HorizontalAlignment="Left"
        Margin="12,432,0,0" Name="btnSaveBill" VerticalAlignment="Top" Width="386"
        FontSize="20" Click="btnSaveBill_Click" />
</Grid>


Step 5: open MainPage.Xaml.cs and perform the following steps:

Use below namespaces:

using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;


Declare below class level Objects/Variables: (Please read comments applied)

//Used for WebAPI Calls
WebClient webClient, webClientMedicine;

//Used to Store Data received from External Calls
MedicineMaster[] Medicines = null;
MedicineMaster SelMedicine = null;

//UI Objects for Dynamic UI Generations
ListBox lstMedicine;
TextBox txtUnitePrice, txtQuantity, txtQuotePrice;

//Collection to STore Item wise Quoet
List<QuoteMaster> lstQueotsList = null;

//Variable to Store Total Bill
static int TotalBill = 0;

Write the helper method to generate the UI for entering the purchase data of every Medicine e.g. MedicineName, Quantity etc:

/// <summary>
/// Helper method to Create Item wise Quoet UI Dynamically
/// </summary>
void CreateQueoteUI()
{
    StackPanel pnl = new StackPanel();
    pnl.Orientation = System.Windows.Controls.Orientation.Horizontal;
    pnl.Width = lstQueote.Width;
    pnl.Height = 80;


     lstMedicine = new ListBox()
    {
         Height=80, Width= 93
    };
    webClientMedicine = new WebClient();
    webClientMedicine.OpenReadCompleted +=
        new OpenReadCompletedEventHandler(webClientMedicine_OpenReadCompleted);
    //Call to WebAPI using URL
    webClientMedicine.OpenReadAsync(new Uri("
http://localhost:51006/api/Medicine"));


    lstMedicine.SelectionChanged +=
        new SelectionChangedEventHandler(lstMedicine_SelectionChanged);
    pnl.Children.Add(lstMedicine);


    txtUnitePrice = new TextBox();
    txtUnitePrice.Height = 80;
    txtUnitePrice.Width = 93;
    txtUnitePrice.IsEnabled = false;
    pnl.Children.Add(txtUnitePrice);


    txtQuantity = new TextBox()
    {
         Height=80,Width=93
    };
    txtQuantity.LostFocus += new RoutedEventHandler(txtQuantity_LostFocus);
    pnl.Children.Add(txtQuantity);


    txtQueotePrice = new TextBox()
    {
        Height = 80,
        Width = 93
    };
    pnl.Children.Add(txtQueotePrice);


    ListBoxItem item = new ListBoxItem();
    item.Content = pnl;
    lstQueote.Items.Add(item); 
}


void txtQuantity_LostFocus(object sender, RoutedEventArgs e)
{
    txtQueotePrice.Text =
        (SelMedicine.UnitePrice * Convert.ToInt32(txtQuantity.Text)).ToString();
    TotalBill = TotalBill + Convert.ToInt32(txtQueotePrice.Text);
    txttotalproce.Text = TotalBill.ToString(); 


    //Save the Queote Item Price in the List
    lstQueotsList.Add(new QueoteMaster()
    {
         MedicineId = Convert.ToInt32(lstMedicine.SelectedValue),
          Quantity = Convert.ToInt32(txtQuantity.Text),
          QueotePrice  = Convert.ToInt32(txtQueotePrice.Text)
    });
}


void lstMedicine_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    try
    {
        int medicineid = Convert.ToInt32(lstMedicine.SelectedValue);
         SelMedicine = (from m in Medicines
                        where m.MedicineId == medicineid
                        select m).First();


        txtUnitePrice.Text = SelMedicine.UnitePrice.ToString();


    }
    catch (Exception)
    {
    }


}


void webClientMedicine_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Result != null)
    {
        //S1: Read the Received Stream
        Stream jsonDataStream = e.Result;
        //S2: JSON Serializer Container
        DataContractJsonSerializer jsonData =
            new DataContractJsonSerializer(typeof(MedicineMaster[]));
        //S3: Read JSON Data and DeSerialize into object MedicineMaster[]
        Medicines = (MedicineMaster[])jsonData.ReadObject(jsonDataStream);
        lstMedicine.ItemsSource = Medicines;
        lstMedicine.DisplayMemberPath = "MedicineName";
        lstMedicine.SelectedValuePath = "MedicineId";
    }
}


 The above method performs the following:

  • Define StackPanel, TextBox and ListBox objects.
  • These objects are added inside the ListBoxItem which is then added in the Items collection of the ListBox.
  • The SelectionChanged event on the ListBox makes call to WebAPI and gets the JSON data which is then Desterilized in the data into array.
  • The TextChanged and LostFocus events performs calculation to complete the business logic.

Call the above method in the Loaded event and also make calls to WebAPI to fetch Stockiest information as below:

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    lstQueotsList = new List<QueoteMaster>();
    webClient = new WebClient();
    webClient.OpenReadCompleted +=
        new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
    webClient.OpenReadAsync(new Uri("
http://localhost:51006/api/Stockiest"));


    CreateQueoteUI();
}


void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    try
    {
        if (e.Result != null)
        {
            Stream jsonDataStream = e.Result;



            DataContractJsonSerializer jsonData =
                new DataContractJsonSerializer(typeof(StockiestMaster[]));
            var Stockiests = (StockiestMaster[])jsonData.ReadObject(jsonDataStream);
            lstStockiest.ItemsSource = Stockiests;
            lstStockiest.DisplayMemberPath = "StockiestName";
            lstStockiest.SelectedValuePath = "StockiestID";


        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message); 
    }
}

On the click event of “+” button, make a call to CreateQuoteUI method to generate UI dynamically.

/// <summary>
/// Method to Create Dynamic UI
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAddNew_Click(object sender, RoutedEventArgs e)
{
    CreateQuoteUI();
}


Write the code in the click event of the ‘Save Bill’ button. This code performs the following:

  • Read the Item wise Quote and the save it in Database table by making POST call to WEB API.
  • The Quote data from the QuoteMaster object is read and Written into memory stream.
  • This memory is then encoded into the string. This is the resultant JSON string data to be Posted to WEB API.
  • For POST operation the WebClient object’s UploadStringAsync method is used.
  • The above same steps are performed for StockBillMaster object also.

/// <summary>
/// Method to Save Final Bill
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSaveBill_Click(object sender, RoutedEventArgs e)
{
    try
    {
        //Call the POST on the Item wise Quote and then on the StockiestBillMaster

        foreach (QuoteMaster quote in lstQuotesList)
        {
            //S1: Generate the JSON Serializer Data
            DataContractJsonSerializer jsonData =
                new DataContractJsonSerializer(typeof(QuoteMaster));
            MemoryStream memStream = new MemoryStream();
            //S2 : Write data into Memory Stream
            jsonData.WriteObject(memStream, quote);
            //S3 : Read the bytes from Stream do that it can then beconverted to JSON String
            byte[] jsonDataToPost = memStream.ToArray();
            memStream.Close();
            //S4: Ehencode the stream into string format
            var data = Encoding.UTF8.GetString(jsonDataToPost, 0, jsonDataToPost.Length);
            //S5: Save Data in the QuoteMaster Table
            WebClient webClientQuote = new WebClient();
            webClientQuote.UploadStringCompleted +=
                new UploadStringCompletedEventHandler(webClientQuote_UploadStringCompleted);
            webClientQuote.Headers["content-type"] = "application/json";
            webClientQuote.UploadStringAsync(new Uri("
http://localhost:51006/api/Quote"),"POST",data);
        }

        Random rnd = new Random();
        StockiestBillMaster finalStockBill = new StockiestBillMaster()
        {
            StockiestBillId = rnd.Next(),
            StockiestID = Convert.ToInt32(lstStockiest.SelectedValue),
            TotalBill = TotalBill
        };

        //S1: Generate the JSON Serializer Data
        DataContractJsonSerializer jsonStockiestData =
            new DataContractJsonSerializer(typeof(StockiestBillMaster));
        MemoryStream memStockiestStream = new MemoryStream();
        //S2 : Write data into Memory Stream
        jsonStockiestData.WriteObject(memStockiestStream, finalStockBill);
        //S3 : Read the bytes from Stream do that it can then beconverted to JSON String
        byte[] jsonStockiestDataToPost = memStockiestStream.ToArray();
        memStockiestStream.Close();
        var stockiestData =
            Encoding.UTF8.GetString(jsonStockiestDataToPost, 0, jsonStockiestDataToPost.Length);

        //S5 : Save the Total Bill
        WebClient webClientTotalBill = new WebClient();
        webClientTotalBill.UploadStringCompleted +=
            new UploadStringCompletedEventHandler(webClientTotalBill_UploadStringCompleted);
        webClientTotalBill.Headers["content-type"] = "application/json";
        webClientTotalBill.UploadStringAsync(new Uri("
http://localhost:51006/api/Bill"), "POST", stockiestData);

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}


void webClientTotalBill_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
   // MessageBox.Show(e.Result);
}

void webClientQuote_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
    //MessageBox.Show(e.Result);
}


Build the application and make sure that it is error free.

Verification:

Make both the projects in the solution as startup (start ASP.NET project and the WP7 project).

Run the application, you will get the Stockiest data and the first dynamic UI for Quote as below:

I4_Res_1

Select any Stockiest from the Stockiest Name, select any Medicine from Medicine List, you will get the Unit Price in the adjusted TextBox of the Medicine list. When you enter Quantity in Quantity Textbox and then Click on the Price TextBox, the Price will be calculated as below:

I5_Res_2

After clicking on “+” you will get the next item added dynamically as below:

I6_Res_3

Now, you can enter the data for the next item and click on save. Te total will update. After clicking on the ‘Save Bill’ button, the Data will be saved in Database.

Conclusion:

With the communication capability between WebAPI and Windows Phone, we can easily develop disconnected application. The client has to carry a lightweight application on his device and any data communication can be easily possible using WebAPI on demand.

The entire source code of this article can be downloaded over here

Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Mark on Tuesday, September 23, 2014 8:16 AM
this does not work. Always returns NotFound

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel