Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

How to Create a Windows 8 App for SharePoint: Part 2 – The Development Stage

5.00/5 (2 votes)
14 Aug 2013CPOL8 min read 15.9K  
Developing a Win8 app and integratinge it with SharePoint Online 2013.

The Development Stage

In my last post How To Create a Windows 8 App For SharePoint Part 1 – The Planning Stage we discussed about the reason I chose Windows 8 HTML5 as my development environment and why it’s the best and the most intuitive environment for SharePoint developers who plan to start creating Win8 apps.

Today we will focus on developing our Win8 app and integratinge it with SharePoint Online 2013. The app will serve as a search and use The new Search REST API to pull data from SharePoint 2013 Search engine (FAST) and display it in our app.

When I decided to connect my Win8 app to SharePoint 2013 Online, it seemed like a straightforward task, all I wanted is to programmatically connect to SharePoint Online services but reality shows otherwise. I thought the answer could be found in one of WinRT’s capabilities integrating with online web services. After exploring this API for several days and even posting a question on Windows Store apps Forums asking “How to call SharePoint Online 2013's REST API from Windows 8 HTML5 App?” unfortunately for me, still haven't got any answer. then I decided it's time to take matters in hand and develop my own custom connector to SharePoint Online services.

Before starting developing any solution, it’s important to understand how SharePoint Online authentication works. The authentication "mechanism" is called Claims based authentication. First we request the token from the STS, we pass the username and password. then the STS returns a security token. after we got our security token we sent it to SharePoint and at last we get our two cookies called “FedAuth” and “rtFa” that we need to pass every time we want to request something from SharePoint.

Luckily for me I found this great article by Wictor Wilén explaining how to do active authentication to Office 365 and SharePoint Online using WCF. Since the code sample provided was targeting framework 3.5, I tweaked it a little bit to work with framework 4.5, called it Office365ClaimsConnector and it worked like a charm.

So let’s get to work Smile

Step 1: creating a blank application

Start VS2012 > Other Languages >JavaScript > Blank App. Name it “Win8AppForSharePoint”

image

Step 2: Creating the Windows Storage & Navigation for our pages

WinRT has a great Storage capabilities and we have access to the following types:

  1. local - data but only on the current device
  2. roaming - data that is available to all devices the user has your app installed on
  3. temporary - data that can be removed by the system at any point after your app is closed

Since our app will only be installed on one device we’ll steak with the local Storage.

Open the “default.html” and add <div id="contentHost"></div> we’ll use this div later for navigation purposes.

Open the “default.js” file and add the following JavaScript code.

JavaScript
// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
    "use strict";

    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;
    var localSettings = Windows.Storage.ApplicationData.current.localSettings;
    var containerName = "cookieContainer1";
    WinJS.strictProcessing();

    app.onactivated = function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {  
                args.setPromise(WinJS.UI.processAll().then(function () {
                    if (isAuthenticated())
                     return WinJS.Navigation.navigate("/pages/Search/search.html");
                    else
                    return WinJS.Navigation.navigate("/pages/Login/login.html");
                }));
           
        }
    };
    WinJS.Navigation.addEventListener("navigated", function (eventObject) {
        var url = eventObject.detail.location;
        var host = document.getElementById("contentHost");
        WinJS.Utilities.empty(host);
        eventObject.detail.setPromise(WinJS.UI.Pages.render(url, host, 
                   eventObject.detail.state).then(function () {
            WinJS.Application.sessionState.lastUrl = url;
        }));
    });
    function isAuthenticated() {
        if (localSettings.containers.hasKey(containerName)) {
            return true;
        }
        return false;
    }
    app.start();
})();

In the isAuthenticated function we're using the Windows.Storage.ApplicationData object to check if our data (user name, password, url) is already stored and navigate to appropriate page. if Authenticated, navigate straight to Search page, else go through the login page.

Step 3: Creating WCF Service

Create a WCF Service Application Project, Name it “Office365ClaimsService”.

image

Create two classes, first a static class called DAL, second class called QueryStringHelper, we’ll use this class later for query formatting.

image

Open Ioffice365ClaimsService.cs and add the following code .

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace Office365ClaimsService
{
    // NOTE: You can use the "Rename" command on the "Refactor"
    // menu to change the interface name "IService1" in both code and config file together.
    [ServiceContract]
    public interface IOffice365ClaimsService
    {

       [OperationContract]
       [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, 
         RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest, 
         UriTemplate = "/authentication")]
        TokenContainer Authentication(string url, string userName, string password);

       [OperationContract]
       [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, 
         RequestFormat=WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest, 
         UriTemplate = "/search")]
       string GetSearchData(string FedAuth, string RtFa, string url, string query);
 
       
    }
    [DataContract]
    public class TokenContainer
    {
        [DataMember]
        public string  FedAuth { get; set; }

        [DataMember]
        public string RtFa { get; set; }
    }
}

We’ll have twp Services, First is an Authentication service that receives the following parameters : url, userName, password. The second service is the search service receiving the following parameters : FedAuth, RtFa, url, query

Since we’re working with JavaScript and handle some sensitive data, The 2 Services use JSON as the Request Format & Response format and the type method is POST.

Open the DAL.cs file and add the following code.

C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using Office365ClaimsConnector;

namespace Office365ClaimsService
{
    public static class DAL
    {
        public static TokenContainer getTokens(string url, string userName, string password)
        {
          TokenContainer tc = new TokenContainer();
          MsOnlineClaimsHelper claimsHelper = new MsOnlineClaimsHelper(url, userName,password);
          CookieContainer cc=claimsHelper.GetCookieContainer();
          tc.FedAuth = cc.GetCookies(new Uri(url))["FedAuth"].Value;
          tc.RtFa = cc.GetCookies(new Uri(url))["rtFa"].Value;
          return tc;
        }


        public static string GetDataFromSP(string FedAuth,string RtFa, 
               string url, QueryType type, string query)
        {
            string responseJson = string.Empty;
            var uri = QueryStringHelper.BuildQuery(QueryType.Search, url, query);
            CookieContainer cc = GetCookieContainer(FedAuth, RtFa, new Uri(url));
            Uri queryUri = new Uri(uri.AbsolutePath);
            var request = HttpWebRequest.CreateHttp(uri);
            request.Method = "GET";
            var accept = "application/json";
            if (accept != null)
                request.Accept = accept;
            request.CookieContainer=cc;
            var response = request.GetResponse();
            Stream res = response.GetResponseStream();
            using (var reader = new StreamReader(res))
            {
                responseJson = reader.ReadToEnd();
            }
            return responseJson;
        }

        private static CookieContainer GetCookieContainer(string FedAuth, string rtFa, Uri uri)
        {
            CookieContainer _cachedCookieContainer = null;
            DateTime _expires = DateTime.MinValue;
              CookieContainer cc = new CookieContainer();
            if (_cachedCookieContainer == null || DateTime.Now > _expires)
            {
                // Set the FedAuth cookie
                Cookie samlAuth = new Cookie("FedAuth", FedAuth)
                {
                    Expires = _expires,
                    Path = "/",
                    Secure = uri.Scheme == "https",
                    HttpOnly = true,
                    Domain = uri.Host
                };
                cc.Add(samlAuth);
                    // Set the rtFA (sign-out) cookie, added march 2011
                    Cookie rtFaCookie = new Cookie("rtFA", rtFa)
                    {
                        Expires = _expires,
                        Path = "/",
                        Secure = uri.Scheme == "https",
                        HttpOnly = true,
                        Domain = uri.Host
                    };
                    cc.Add(rtFaCookie);
                }
                _cachedCookieContainer = cc;
                return cc;
            }
        }
    }

You’ll need to add a reference to Office365ClaimsConnector project. as a reminder, Office365ClaimsConnector is the code sample I found in this great article by Wictor Wilén that actually does all the Authentication work using WCF bindings and contracts.

Using method GetToken we pass the following parameters: url, username and password to our Office365ClaimsConnector.MsOnlineClaimsHelper class that eventually will return our tokens to the service and back to our client.

The method GetDataFromSP is responsible for our query request, it accepts the following parameters: FedAuth,RtFa, url, search type and query. basically we pass the 2 tokens to the CookieContainer object and finally to the HttpWebRequest object along with the url and the query. If our request succeeded we’ll get our search results in a nice JSON format.

Open the QueryStringHelper.cs file and add the following code.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Office365ClaimsService
{
    public enum QueryType
    {
        Search,
        Lists,
    }
    public static class QueryStringHelper
    {
        public static Uri BuildQuery(QueryType type,string url,string query)
        {
            UriBuilder bldr = new UriBuilder(url);
            switch (type)
            {
                case QueryType.Search:
                    ///_api/search/query?querytext='search is cool'
                    string restQuery = "/_api/search/query";
                    bldr.Path +=restQuery;
                    bldr.Query = "querytext='" + query + "'";
                    break;
                case QueryType.Lists:
                    break;
                default:
                    break;
            }
            return bldr.Uri;
        }
    }
}

I created this class for future implementation for other scenarios besides Search, like CRUD operations for lists etc..

Step 4: creating Login page

in “Win8AppForSharePoint" project Create a folder called pages and inside that folder create another folder called Login with 3 files: login.css ,login.html and login.js

image

Open the “login.html” file and add the following HTML:

XML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Connect to Sharepoint Online</title>
    
    <!-- WinJS references -->
    <link rel="stylesheet" href="http://www.codeproject.com/Microsoft.WinJS.1.0.RC/css/ui-light.css" />
    <script  src="http://www.codeproject.com/Microsoft.WinJS.1.0.RC/js/base.js"></script>   1:  
   2:     <script  src="http://www.codeproject.com/Microsoft.WinJS.1.0.RC/js/ui.js">
   1: </script>
   2:     
   3:     <link rel="stylesheet" href="http://www.codeproject.com/pages/Login/login.css" />
   4:     <script  src="http://www.codeproject.com/pages/Login/login.js">
</script>

</head>
<body>
    <!-- The content that will be loaded and displayed. -->
    <div class="detailPage fragment">
        <header role="banner" aria-label="Header content">
        
            <div class="titleArea">
                <h1 class="pageTitle win-type-xx-large"></h1>
            </div>
        </header>
        <section role="main" aria-label="Main content">
            <article>
                <div>
                    <header>
                        <h2 class="title win-type-x-large"></h2>
                    </header>
                    <div class="image"></div>
                    <div class="content"></div>
                    <!-- input table -->
                    <table border="0" style="width: 252px; height: 60px">
                            <tr>
                            <td><label>Address:</label></td>
                            <td><input id="address" type="text"/></td>
                          </tr>
                          <tr>
                            <td><label>Customer ID:</label></td>
                            <td><input id="username" type="text"/></td>
                          </tr>
                          <tr>
                            <td><label>Password:</label></td>
                            <td style="height: 40px"><input id="password" type="password"/></td>
                          </tr>
                          <tr>
                            <td>
                              <label>
                                <input class="submitButton" type="submit" name="Submit" value="Submit" />
                              </label>
                            </td>
                          </tr>
                           <tr>
                            <td>
                                <progress class="win-ring win-large progress"></progress>    
                            </td>
                          </tr>
                    </table>
                </div>
            </article>
        </section>
    </div>
</body>
</html>

The HTML file holds a simple table containing the login box that has Address , User id and Password. it also containing a progress control (out of the box), I also added another class called ‘progress’ to hide the control and when the Login button is pressed and our request is sent to a remote server to get the access tokens, we’ll display the loading image.

image

Open the “login.css” file and add the following css.

CSS
.detailPage section[role=main] {
    -ms-grid-row: 2;
    display: block;
    width: 100%;
    height: 100%;
    overflow-x: auto;
}

.detailPage section[role=main] article {
    column-fill: auto;
  
    column-width: 800px;
  
    column-gap: 80px;
    height: auto;
    width: 800px;
    margin-left: 120px;
}

.detailPage section[role=main] h1 {
    margin-top: 0px;
    margin-bottom: 20px;
}

.detailPage section[role=main] .image {
    width: 800px;
    height: 100px;
    /*background: rgba(209, 211, 212, 1);*/
    /*background: rgba(144, 178, 41, 1);*/
}

.detailPage section[role=main] p {
    margin-right: 20px;
}

@media screen and (-ms-view-state: snapped) {
.detailPage .win-contentTitle {
    font-size: 11pt;
    line-height: 15pt;
}

.detailPage header[role=banner] {
    display: -ms-grid;
  
    -ms-grid-columns: 44px 1fr;
  
    -ms-grid-rows: 1fr;
}

.detailPage section[role=main] .image {
    width: 260px;
    height: 140px;
}

.detailPage section[role=main] article {
     display: -ms-grid;
  
    -ms-grid-columns: 290px 1fr;
  
    -ms-grid-rows: 1fr;
    -ms-grid-row: 2;
    width: 290px;
    margin-left: 20px;
    overflow-y: auto;
    overflow-x: hidden;
}

}

.progress {
    display:none;
}

The Login box:

image

Open the “login.js” file and add the following JavaScript code.

JavaScript
(function () {

    // Track if the log in was successful
    var loggedIn;
    var localSettings = Windows.Storage.ApplicationData.current.localSettings;
    var containerName = "exampleContainer1";
    var nav = WinJS.Navigation;
    var container;
    var FedAuth;
    var RtFa;
    var address;
    var username;
    var password;
    "use strict";
    var page = WinJS.UI.Pages.define("/pages/Login/login.html", {
        ready: function (element, options) {
            element.querySelector('.submitButton').addEventListener('click', submitLogin, false);      
        }
    });

    function submitLogin() {
        try {
            if (address|| username|| password)
            {
                //Creating message dialog box
                var messagedialogpopup = new Windows.UI.Popups.MessageDialog(
                  "Input can not be empty!", "Error");
                messagedialogpopup.showAsync();
                return;
            }
            document.querySelector(".progress").style.display = "block";
             address = document.getElementById("address").value;
             username = document.getElementById("username").value;
             password = document.getElementById("password").value;
            var json = JSON.stringify({ "url": address, 
              "userName": username, "password": password });
        }
        catch (err) {       
        }
        WinJS.xhr({
            type: "POST",
            url: "http://localhost:2738/Office365ClaimsService.svc/Authentication",
            headers: { "content-type": "application/json; charset=utf-8" },
            data: json,
        }).done(loginSuccess, loginFaliure, loginProgress);
    };
    function loginSuccess(request)
    {
        var obtainedData = window.JSON.parse(request.responseText);
        var container = localSettings.createContainer(containerName, 
              Windows.Storage.ApplicationDataCreateDisposition.always);
        if (localSettings.containers.hasKey(containerName)) {
            localSettings.containers.lookup(containerName).values["FedAuth"] = obtainedData.FedAuth;
            localSettings.containers.lookup(containerName).values["RtFa"] = obtainedData.RtFa;
            localSettings.containers.lookup(containerName).values["Url"] = address;
        }
        WinJS.Navigation.navigate('/pages/Search/search.html');
    }
    function loginFaliure(request)
    {
        document.querySelector(".progress").style.display = "none";
        //Creating message dialog box
        var messagedialogpopup = new Windows.UI.Popups.MessageDialog(
              "An error occurred!", "Error");
        messagedialogpopup.showAsync();
        return false;
    }
    function loginProgress(request) {
      
    }
})();

Using the WinJS.UI.Pages.define object we basically get all HTML elements located on the page we requested. using querySelector we get the Submit button element and add the EventListener for our button click. if one of the fields is empty we add MessageDialog telling the user that his input is empty. After collecting the data (address, username, password) from the Login box, we create a JSON text using JSON.stringify and pass it to our WCF Authentication service, using WinJS.xhr function we get a Promise(callback). if we successfully logged-in we’ll get our two tokens/cookies “FedAuth” and “rtFa” that we’ll use for all of ours requests from SharePoint Online.

It’s worth mentioning that you can’t use “alert” for debugging or any pop-up notifications anymore as WinJS doesn't support it. for debugging use console.log() and for notification use MessageDialog.

We create a key for the Storage object we talked about, pass our tokens and url to the Storage object and navigate to Search page.

Step 5: creating Search page, Template and Binding

in “Win8AppForSharePoint" project create a folder called Search with 3 files: search.css ,search.html and search.js

image

Open the “search.html” file and add the following html.

XML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>SharePoint Search</title>

    <!-- WinJS references -->
    <link href="http://www.codeproject.com/Microsoft.WinJS.1.0.RC/css/ui-light.css" rel="stylesheet" />
    <script src="http://www.codeproject.com/Microsoft.WinJS.1.0.RC/js/base.js"></script>   1:  
   2:     <script src="http://www.codeproject.com/Microsoft.WinJS.1.0.RC/js/ui.js">
   1: </script>
   2:   
   3:     <!-- Win8AppForSharePoint references -->
   4:     <link href="http://www.codeproject.com/css/default.css" rel="stylesheet" />
   5:     <link rel="stylesheet" href="http://www.codeproject.com/pages/Search/search.css" />
   6:     <script  src="http://www.codeproject.com/pages/Search/search.js">
   1: </script>
   2:     <script src="http://www.codeproject.com/js/default.js">
   1: </script>
   2:     <script src="http://www.codeproject.com/js/navigator.js">
</script>

</head>
<body>   
       
          <div  class="content" aria-label="Main content" role="main">
              <img class="spLogo" src="../../images/sharepoint-logo.jpg" />
            <div id="searchLbl">SharePoint Online Search</div>
         <input id="searchBox" name="search" type="text" placeholder="Search..." />
              <button id="searchBtn" type="button">Search</button> 
             <div><progress class="win-ring win-large progress"></progress></div>

        <div id="searchResultsTemplate" data-win-control="WinJS.Binding.Template">
            <div class="itemResult">
                <div><h2><a class="resultTitleLink" data-win-bind="innerText: title;source: url"></a></h2></div>
                <span> <a class="resultUrl" data-win-bind="innerText: url;source: url" ></a>
                    <span class="resultDate" data-win-bind="innerText: date"></span>
                </span>                
                <div class="resultContent" data-win-bind="innerText: content"></div>
                
                
            </div>
        </div>    
              <div id="searchListView" data-win-control="WinJS.UI.ListView" 
        data-win-options="{itemTemplate:searchResultsTemplate,selectionMode:'none', layout: { type: WinJS.UI.ListLayout }}"></div>        
          </div>
</body>
</html>

The HTML will hold 3 section:

  1. HTML elements containing our Search box and the “Search” button.
  2. DIV marked with data-win-control="WinJS.Binding.Template" attribute tells WinJS to treat it like a template, element marked with data-win-bind=" " attribute inside the template will help the binding engine to know which JavaScript properties from the data source to map to the appropriate HTML. read more about it here.
  3. DIV marked with data-win-control="WinJS.UI.ListView" attribute *transforms this simple DIV to JavaScript ListView control. inside the ListView control we have another attribute called data-win-options="{}" that we’ll use to tell the control which template to bind, which Layout etc.. read more about it here.

*This operation is done thanks to a JavaScript code: WinJS.UI.processAll();. Since we added the ListView to a Page control, we don't need to call WinJS.UI.processAll because the Page control does it for us.

Open the “search.css” file and add the following CSS:

CSS
#searchBox {
    width: 500px;
    height: 35px;
    padding: 10px 20px 10px 10px;
    margin: 1px auto 50px;

}
#searchLbl {
  font-family:'Segoe UI Symbol';
  font-weight: bold;

}

.content
{
    text-align: center; padding-top: 50px;

}
.pagetitle{


}
.resultTitleLink{
    text-decoration: underline;


}
.resultAuthor{


}
.resultDate{
color:#777;
font-size: small;
}
.resultUrl{
color:#388222;
font-size: small;

}
.resultContent {
    font-size: small;
}
.spLogo {
    height: 103px;
}
.progress {
    display:none;
    margin: 1px auto 50px;
}

I also added an Office image and the Search box got a nice and simple search based UI :

image

Open the “search.js” file and add the following JavaScript code.

(function () {
    "use strict";
    var localSettings = Windows.Storage.ApplicationData.current.localSettings;
    var containerName = "exampleContainer1";
    var searchResults = new Array();
    var page = WinJS.UI.Pages.define("/pages/Search/search.html", {
        ready: function (element, options) {
            document.getElementById("searchBtn").addEventListener("click", getSearchData, false);
        }
    });

    function getSearchData()
    {  
        if (localSettings.containers.hasKey(containerName)) {
            var FedAuth = localSettings.containers.lookup(containerName).values["FedAuth"];
            var RtFa = localSettings.containers.lookup(containerName).values["RtFa"];
            var Url = localSettings.containers.lookup(containerName).values["Url"];
            var Query = document.getElementById("searchBox").value;
            if (Query == "") {        
                //Creating message dialog box
                var messagedialogpopup = new Windows.UI.Popups.MessageDialog("Input can not be empty!","Error");
                messagedialogpopup.showAsync();
                return;
            }
            var lstView = document.getElementById("searchListView").winControl;
            //clear array items
            searchResults = [];
            var dataList = new WinJS.Binding.List(searchResults);
            lstView.itemDataSource = dataList.dataSource;
            document.querySelector(".progress").style.display = "block";
            var json = JSON.stringify({ "FedAuth": FedAuth, "RtFa": RtFa, "url": Url, "query": Query });
            WinJS.xhr({
                type: "POST",
                url: "http://localhost:2738/Office365ClaimsService.svc/search",
                headers: { "content-type": "application/json; charset=utf-8" },
                data: json,
            }).done(processData, dataError);

        }
    }


    function processData(response) {
        document.querySelector(".progress").style.display = "none";
   
        var data = JSON.parse(response.response);
        data = JSON.parse(data);
     
        var results = data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;
        for (var i = 0, len = results.length; i < len; i++) {
            var item = results[i].Cells;
            var date = new Date(item.results[8].Value);
            var resultItem = {
                author: item.results[4].Value,
                title: item.results[3].Value,
                date: date.toDateString(),
                url: item.results[6].Value,
                content: item.results[10].Value,
            };
            searchResults.push(resultItem);
        }
        var lstView = document.getElementById("searchListView").winControl;   
        var dataList = new WinJS.Binding.List(searchResults);
        lstView.itemDataSource = dataList.dataSource;

    }
    function dataError(data)
    {
        document.querySelector(".progress").style.display = "none";
        //Creating message dialog box
        var messagedialogpopup = new Windows.UI.Popups.MessageDialog("Error!", "Error");
        messagedialogpopup.showAsync();
        var d = data;
    };   }
})();

After we got all the html elements using WinJS.UI.Pages.define object. we get the Search button element and add the EventListener for our button click. We grab all the data from our storage object using a key , of course if our query is empty we’ll throw a pop-up notifying the user .

Since our search data will change every time we click our search button we need to send an empty array list to ListView control to clear the data. we use a binding list that excepts our JavaScript array and connecting it to ListView itemDataSource to display the data. after collecting the data ( FedAuth,RtFa, url, query ) from the storage object, we create a JSON text using JSON.stringify and pass it to our WCF Search service we talked about earlier to pull data from SharePoint Online using Search REST API. if the operation succeeded we get our JSON string, then we parse it and start populating the JavaScript list array, init our binding list with our data and connect it to ListView.itemDataSource to display the content that will be showed in a template we created on the HTML page.

The final result:

image

Step 6: Summary

Today we successfully created our Windows 8 App integrated with SharePoint Online services. We programmatically connected to SharePoint using Claims-based Authentication, got our data with the help of SharePoint search REST API and displayed our search results. of course for displaying the search results I could iterate the JSON object and start appending the HTML elements but instead we used the Template and the Binding capabilities to render our data in an easy and simple way that WinJS offers us.

We've only seen the tip of the iceberg, Windows 8 app development is a rich and exciting world so I strongly advise you to start exploring it Smile.

Soon we’ll try to extend this app to interact with the operation system.

You can download the full project on CodePlex.

I hope this post was helpful .

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)