Partager via


Windows Store Application Creation

Updated: April 9, 2014

Applies To: Windows Server 2012 R2

Windows Store Application creation and testing

Now it is time to create our second application that will access our ToDoListService. This application is a Windows Store App. This section contains instructions on the following:

  • ToDoListClient_WSA Application Overview

  • Create the ToDoListClient_WSA Application Project

  • Obtain Redirect Uri

  • Register the ToDoListClient_WSA Application in AD FS

  • Setting Required Capabilities for ToDoListClient_WSA

  • Adding code to the MainPage.xaml

  • Add Code to our Code-Behind Page

  • Add the ToDoList Item class

  • Test Windows Store Application

ToDoListClient_WSA Application Overview

The following section is just a brief overview of the Windows Store application that we are about to create. This application is very similar to the WPF application in that it contains 2 buttons, 1 textbox, and 1 gridview. One button is used to retrieve the items that are in our ToDoListService, the other button is used to add items to our ToDoListService. The textbox is used for the input that is going to be added and the gridview shows our ToDoList items.

Windows Store App

Here is a pseudo flow chart of how the code works. The code is very much like the code we used with our WPF client with a few minor differences. For instance, the Windows Store Application does not support the JavaScriptSerializer so we opted to use the DataContractJsonSerializer. You can use the flow below as a reference as you work with the code.

Windows Store App Flow

Create the ToDoListClient_WSA Application Project

Because there are a few more steps with regard to the Windows Store Application, we are going to break it down into pieces so that it is easier to follow. The first thing that we are going to do is to create our project in Visual Studio 2013. Use the following steps to create the ToDoListClient_WSA project.

  1. In Visual Studio 2013, select New Project.

  2. On the left, under Visual C#, select Windows Store, select Blank App (XAML) and rename the project to ToDoListClient_WSA. Click Ok.

    Store 1

  3. You will be prompted to obtain a developer license. Click I Agree.

    Store 2

  4. This will bring up a User Account Control dialog box, click Yes. Then you will be asked to sign-in.

    Store 3

  5. Using a Microsoft account (formerly a live id), sign-in. You will receive a message box stating that you have a license and that it expires on such and such date. Click Close.

  6. On the right, in Solution Explorer, right-click ToDoListClient_WSA (Windows 8.1) and select Manage NuGet Packages. This will bring up the Manage NuGet Packages window.

  7. On the left, click Online and in the search box on the right enter aal.

  8. From the list that comes up, select Windows Azure Authentication Library for Windows Store Apps and click Install.

    Warning

    Please be aware that his code is the same as the code in ADAL but it is still in preview for Windows Store Apps. Do not use this code in production as it will change prior to RTM.

    Store 5

  9. On the License Acceptance screen, click I Accept. This will install the package. Click Close.

Obtain Redirect Uri

Before we can register our Windows Store Application we need to obtain the redirect Uri Use the following procedure to obtain the redirect Uri.

  1. Add the following code to the MainPage.xaml.cs file.

        protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                Uri redirectUri = Windows.Security.Authentication.Web.WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
            }
    
  2. Set a break point just below the Uri redirectUri line. This will allow us to obtain the redirect uri which we will need to register in AD FS.

    Breakpoint

  3. Press F5 to run our Windows Store Application.

  4. Once the breakpoint is hit, down under Autos, expand redirecturi and select AbsoluteUri and click the magnifying glass next to it. This will bring up a dialog box with the uri in it. Copy this down for use later in the code and register it in AD FS.

    Store 17

  5. Go ahead and stop the debugger.

  6. Go ahead and remove the break point that we set and remark out the Uri line so it looks like the following:

      protected override void OnNavigatedTo(NavigationEventArgs e)
            {
               // Uri redirectUri = Windows.Security.Authentication.Web.WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
            }
    

Register the ToDoListClient_WSA Application in AD FS

Now we will register the Windows Store Application with AD FS.

To register our ToDoListClient_WSA Application

  1. Open Windows PowerShell and enter the following:

    Add-ADFSClient -Name “ToDoListClient_WSA” -ClientId “23A77FA6-6951-4611-AEE3-99F5936E9C85 ″ -RedirectUri “ms-app://s-1-15-2-3998261941-229882958-3441403557-238702382-3905670911-2342015709-1005871506/”
    

    Store 18

  2. Close PowerShell.

Setting Required Capabilities for ToDoListClient_WSA

In order for AAL to be able to work in the ToDoListClient_WSA application, the following capabilities must be granted to the application.

  • Enterprise Authentication

  • Internet (Client)

  • Private Networks (Client & Server)

  • Shared User Certificates

Use the following steps to set the capabilities.

To set the required capabilities

  1. In Visual Studio Solution Explorer, right-click Package.appxmanifest and select Open.

  2. At the top, select Capabilities.

  3. Scroll down and on the left place a check in all of the capabilities listed above.

    Store 21

Adding code to the MainPage.xaml

Now it is time to add code to the MainPage.xaml file. Open the MainPage.xaml file and the following so that your xaml file looks like the code below.

<Page
    x:Class="ToDoListClient_WSA.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ToDoListClient_WSA"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <CollectionViewSource
            x:Name="ToDoCollection" IsSourceGrouped="false"/>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <GridView Name="gvToDo" HorizontalAlignment="Left" BorderThickness="1" BorderBrush="White" VerticalAlignment="Top" Width="1254" Height="426"  ItemsSource="{Binding Source={StaticResource ToDoCollection}}" SelectionChanged="GridView_SelectionChanged" Margin="61,118,0,0">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid HorizontalAlignment="Left" Width="250" Height="250">
                        <Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
                            <Image Source="Assets/MediumGray.png" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
                        </Border>
                        <StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
                            <TextBlock Text="{Binding Title}" FontFamily="Segoue UI" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextBlockStyle}" Height="60" >

                            </TextBlock>

                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>



        </GridView>

        <TextBox Name="txtToDoItem" HorizontalAlignment="Left" Margin="61,669,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="399"/>
        <Button Content="Add" HorizontalAlignment="Left" Margin="487,643,0,0" VerticalAlignment="Top" Height="86" Width="155" Click="Button_Add"/>
        <Button Content="Get" HorizontalAlignment="Left" Margin="686,643,0,0" VerticalAlignment="Top" Height="86" Width="155" Click="Button_Get"/>
        <TextBlock  HorizontalAlignment="Left" Margin="62,41,0,0" TextWrapping="Wrap" Text="To Do Items" VerticalAlignment="Top" Height="73" Width="541" FontSize="56"/>

    </Grid>
</Page>

This code adds all of the items that our application will use. The buttons, the textbox, the gridview and a text block that is used for the header. We also add the Click events to our buttons and the GridView_SelectionChanged event which can be used should the items in our list change.

Warning

The image source file above was copied from a pre-existing Windows Store App. It is added by default to any Windows Store App project that uses the Grid App template. If you do not have one, you can create a Grid App and then add the MediumGray.png to the ToDoListClient_WSA project or you can use your own image.

Add Code to our Code-Behind Page

Now we will add the code to our code behind page that will be responsible for interacting with the ToDoListService, Open MainPage.xaml.cs and make sure that the code looks like the code below.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Microsoft.Preview.WindowsAzure.ActiveDirectory.Authentication;
using Windows.Security.Authentication.Web;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net;
using Windows.UI.Popups;
using System.Runtime.Serialization.Json;
using System.Text;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238

namespace ToDoListClient_WSA
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        string authheader;
        System.Net.Http.HttpResponseMessage myResponse;
        public MainPage()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
               //  Uri redirectUri = Windows.Security.Authentication.Web.WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
        }
        private async void GetToDoList()
        {
            authheader = null;
            GetAuthorizationHeader();
           

            if (authheader == null)
            {
                return;
            }
            try
            {
                HttpClient client = new HttpClient();
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44300/api/TodoList");
                request.Headers.TryAddWithoutValidation("Authorization", authheader);
                HttpResponseMessage response = await client.SendAsync(request);
                string responseString = await response.Content.ReadAsStringAsync();

                var json = new DataContractJsonSerializer(typeof(List<ToDoItem>));

                Encoding enc = new UTF8Encoding(true, true);
                byte[] bytes = enc.GetBytes(responseString);

                var stream = new MemoryStream(bytes);

                var mylist = json.ReadObject(stream) as List<ToDoItem>;
                List<ToDoItem> toDoArray = new List<ToDoItem>(mylist);

                ToDoCollection.Source = toDoArray;
                
            }
            catch (WebException ex)
            {
                DisplayError(((HttpWebResponse)(ex.Response)).StatusCode);
            }
            catch (Exception ex)
            {
                MessageDialog error = new MessageDialog((ex.ToString()));
            }
        }

        private async void GetAuthorizationHeader()
        {
            string authority = "https://adfs.corp.contoso.com/adfs";
            string resourceURI = "http://corp.contoso.com/ToDoListService";
            string clientID = "23A77FA6-6951-4611-AEE3-99F5936E9C85";
            string clientReturnURI = "ms-app://s-1-15-2-3998261941-229882958-3441403557-238702382-3905670911-2342015709-1005871506/";


  

            try
            {
                AuthenticationContext authenticationContext =
                new AuthenticationContext(authority, false);

                AuthenticationResult _authResult = await authenticationContext.AcquireTokenAsync(resourceURI, clientID, clientReturnURI, "", "");
                authheader = _authResult.AccessToken.ToString();
               
            }
            catch (Exception ex)
            {
                string message = ex.Message;

                if (ex.InnerException != null)
                {
                    message += "InnerException : " + ex.InnerException.Message;
                }

                MessageDialog error = new MessageDialog(message); 
            }

          
        }

        void DisplayError(HttpStatusCode statusCode)
        {
            switch (statusCode)
            {
                case HttpStatusCode.Unauthorized:
                    {
                        // An unauthorized error occurred, indicating the security token provided did not satisfy the service requirements
                        
                        MessageDialog result = new MessageDialog("You are not authorized to access the ToDoListService.");

                     
                        
                     
                    }
                    break;
                default:
                    MessageDialog newresult = new MessageDialog("Sorry, accessing your ToDo list has hit a problem.");
                    break;
            }
        }

        private void GridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }

        private async void SetResponseFromService(string authorizationHeader, ToDoItem item)
        {
            string httpRequestMethod = "POST";
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://localhost:44300/api/TodoList");
            request.Method = httpRequestMethod;
            request.ContentType = "application/json";

            // add the access token to the http authorization header on the call to access the resource.                      
            request.Headers["Authorization"] = authorizationHeader;

            HttpContent content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Title", item.Title) });
           
            HttpClient httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Authorization =
                 new AuthenticationHeaderValue("Bearer", authorizationHeader);

            // Call the TodoListService
            var response = await httpClient.PostAsync("https://localhost:44300/api/TodoList", content);
            myResponse = response;
        }
        private void Button_Add(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(txtToDoItem.Text))
            {
                MessageDialog newresult = new MessageDialog("Please enter a value for the to-do item name."); 
                return;
            }

            ToDoItem item = new ToDoItem();
            item.Title = txtToDoItem.Text;

            authheader = null;
            GetAuthorizationHeader();

            if (authheader == null)
            {
                return;
            }

            SetResponseFromService(authheader, item);
            txtToDoItem.Text = "";
            GetToDoList();



        }

        private void Button_Get(object sender, RoutedEventArgs e)
        {
            GetToDoList();
        }
    }
}

Now we will look at the code, a little more in depth. First thing we added was the following additional using statements.

using Microsoft.Preview.WindowsAzure.ActiveDirectory.Authentication;
using Windows.Security.Authentication.Web;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net;
using Windows.UI.Popups;
using System.Runtime.Serialization.Json;
using System.Text;

Next, we add two variables. One will be used to hold the authorization header as a string and the other will hold an HttpResponseMessage.

    public sealed partial class MainPage : Page
    {
        string authheader;
        System.Net.Http.HttpResponseMessage myResponse;
        public MainPage()
        {
            this.InitializeComponent();

Now, we add the following GetToDoList code. This code is very similar to the code we used in our WPF application. It gets the authorization header, constructs an HTTP GET request, and ultimately sets our ToDoCollection.Source equal to the List of ToDoItems.

        private async void GetToDoList()
        {
            authheader = null;
            GetAuthorizationHeader();
           

            if (authheader == null)
            {
                return;
            }
            try
            {
                HttpClient client = new HttpClient();
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44300/api/TodoList");
                request.Headers.TryAddWithoutValidation("Authorization", authheader);
                HttpResponseMessage response = await client.SendAsync(request);
                string responseString = await response.Content.ReadAsStringAsync();

                var json = new DataContractJsonSerializer(typeof(List<ToDoItem>));

                Encoding enc = new UTF8Encoding(true, true);
                byte[] bytes = enc.GetBytes(responseString);

                var stream = new MemoryStream(bytes);

                var mylist = json.ReadObject(stream) as List<ToDoItem>;
                List<ToDoItem> toDoArray = new List<ToDoItem>(mylist);

                ToDoCollection.Source = toDoArray;
                
            }
            catch (WebException ex)
            {
                DisplayError(((HttpWebResponse)(ex.Response)).StatusCode);
            }
            catch (Exception ex)
            {
                MessageDialog error = new MessageDialog((ex.ToString()));
            }
        }

Similarly, the next bit of code is also very much like the code in the WPF application. In GetAuthorizationHeader we contact the AD FS server and acquire our JWT token, then we set the authheader string equal to the token we acquired.

        private async void GetAuthorizationHeader()
        {
            string authority = "https://adfs.corp.contoso.com/adfs";
            string resourceURI = "http://corp.contoso.com/ToDoListService";
            string clientID = "23A77FA6-6951-4611-AEE3-99F5936E9C85";
            string clientReturnURI = "ms-app://s-1-15-2-3998261941-229882958-3441403557-238702382-3905670911-2342015709-1005871506/";


  

            try
            {
                AuthenticationContext authenticationContext =
                new AuthenticationContext(authority, false);

                AuthenticationResult _authResult = await authenticationContext.AcquireTokenAsync(resourceURI, clientID, clientReturnURI, "", "");
                authheader = _authResult.AccessToken.ToString();
               
            }
            catch (Exception ex)
            {
                string message = ex.Message;

                if (ex.InnerException != null)
                {
                    message += "InnerException : " + ex.InnerException.Message;
                }

                MessageDialog error = new MessageDialog(message); 
            }

          
        }

The next bit of code is very similar to the GetResponseFromService code in the WPF application and is used to post an item to our ToDoListService. This code takes the header and item it was passed, constructs an HTTP POST message and then sets the HttpResponseMessage myResponse equal to the response that is received from the request.

private async void SetResponseFromService(string authorizationHeader, ToDoItem item)
        {
            string httpRequestMethod = "POST";
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://localhost:44300/api/TodoList");
            request.Method = httpRequestMethod;
            request.ContentType = "application/json";

            // add the access token to the http authorization header on the call to access the resource.                      
            request.Headers["Authorization"] = authorizationHeader;

            HttpContent content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Title", item.Title) });
           
            HttpClient httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Authorization =
                 new AuthenticationHeaderValue("Bearer", authorizationHeader);

            // Call the TodoListService
            var response = await httpClient.PostAsync("https://localhost:44300/api/TodoList", content);
            myResponse = response;
        }

The rest of the code is almost identical to the code we saw in the WPF application. The event handler for the Get button simply calls GetToDoList and the Add button follows the same logic we saw earlier. Now let’s move on to testing our application.

Add the ToDoList Item class

The last thing that we need to do is to add a ToDoItem class to our ToDoListClient_WSA project. There are two ways that this can be accomplished. On the project, you can right-click, select Add, and add a new class with the following code:

namespace ToDoListClient_WPF
{
    public class ToDoItem
    {
        public string Title { get; set; }

    }
}

Or you can import the class that we made for the ToDoListService or ToDoListClient_WPF and then change the namespace to reflect the ToDoListClient_WSA. Either way, once this is done, our client is ready for testing.

Test Windows Store Application

Now we can test our Windows Store Application.

To test our Windows Store Application

  1. First, we need to start our ToDoListService, so open an instance of Visual Studio 2013, select Open Project and navigate to the ToDoListSerivce project, open it if it is not already open and hit F5..

  2. Back in our ToDoListClient_WSA application, hit F5.. This will launch our Windows Store app.

  3. Since we don’t have anything in our ToDoListService yet, enter Walk the Dog in the box and click Add

  4. You will see the Contoso login page. Enter Britta Simon’s username and password and click Sign in

    Enable Loopback

  5. Once this has completed, you should see the Walk the Dog item in the grid.

    Test Store 3