WPF Application Creation
Updated: April 9, 2014
Applies To: Windows Server 2012 R2
WPF Application creation and testing
Now it is time to create our first application that will access our ToDoListService. This application is a rich client WPF application. This section contains instructions on the following:
ToDoListClient_WPF Application Overview
Register the ToDoListClient_WPF Application
Create the ToDoListClient_WPF Application
Test the ToDoListClient_WPF
ToDoListClient_WPF Application Overview
The following section is just a brief overview of the WPF application that we are about to create. It is a simple application with 2 buttons, a textbox, and a data grid. 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 datagrid shows our ToDoList items as well as a checkbox that can be used to mark when they are complete.
Here is a pseudo flow chart of how the code works. You can use this as a reference as you apply the code below.
Register the ToDoListClient_WPF Application
First we need to register our WPF app in AD FS. In AD FS, OAuth clients are registered via PowerShell.
To register our ToDoListClient_WPF application in AD FS
Open Windows PowerShell and enter the following:
Add-ADFSClient -Name “ToDoListClient_WPF” -ClientId “4BD0D3C2-97DF-408B-A6D4-A8FCF84030C0″ -RedirectUri “http://wpfclienturi/”
Warning
Be sure to make the redirecturi unique, even though it is arbitrary for a rich client. These must be unique in AD FS. Also, the ClientId was created using the Create GUID tool in Visual Studio. You can create your own Guid if so desired.
Close PowerShell.
Create the ToDoListClient_WPF Application
Now we will create the WPF application in Visual Studio 2013.
To create the ToDoListClient_WPF Application
Open Visual Studio 2013.
Select New Project.
From the list of projects, navigate to Visual C#, select Windows and choose WPF Application.
In the Name box, remove WpfApplication1 and enter ToDoListClient_WPF
On the right, in Solution Explorer, right-click ToDoListClient_WPF and select Manage NuGet Packages. This will bring up the Manage NuGet Packages window.
On the left, click Online and in the search box on the right enter adal.
From the list that comes up, select Active Directory Authentication Library and click Install.
On the License Acceptance screen, click I Accept. This will install the package. Click Close.
On the right, in Solution Explorer, right-click ToDoListClient_WPF, select Add and then select Reference. This will bring up the Reference Manager.
Scroll down to System.Net.Http and place a check in the box next to it. Also place a check in System.Web and System.Web.Extensions. Click Ok.
We are now going to add the xaml code to our MainWindow, so in Solution Explorer, select MainWindow.Xaml. In the XAML window, copy the following xaml code between <Grid> and </Grid>.
<Button Name="btnGet" Content="Get" HorizontalAlignment="Left" Margin="310,237,0,0" VerticalAlignment="Top" Width="75" Click="Button_Get"/> <Button Name="btnAdd" Content="Add" HorizontalAlignment="Left" Margin="201,237,0,0" VerticalAlignment="Top" Width="75" Click="Button_Add"/> <TextBox Name="txtToDoItem" HorizontalAlignment="Left" Height="23" Margin="10,234,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="170"/> <DataGrid Name="dgToDoItems" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="196" Width="497" > <DataGrid.Columns> <DataGridCheckBoxColumn x:Name="TaskComplete" Header="Task Complete"/> </DataGrid.Columns> </DataGrid>
This code creates our buttons, textbox and our datagrid that will show our ToDoList Items.
Now in Solution Explorer, expand MainWindow.xaml and select the code-behind page, MainWindow.xaml.cs. Add the following code so that your MainWindow.xaml.cs looks like below.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Net.Http; using Microsoft.IdentityModel.Clients.ActiveDirectory; using System.Net; using System.Web.Script.Serialization; using System.Data; using System.IO; namespace ToDoListClient_WPF { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void GetToDoList() { string authorizationHeader = GetAuthorizationHeader(); if (authorizationHeader == null) { return; } try { HttpClient client = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44300/api/ToDoList"); request.Headers.TryAddWithoutValidation("Authorization", authorizationHeader); HttpResponseMessage response = await client.SendAsync(request); string responseString = await response.Content.ReadAsStringAsync(); JavaScriptSerializer serializer = new JavaScriptSerializer(); List<ToDoItem> toDoArray = serializer.Deserialize<List<ToDoItem>>(responseString); dgToDoItems.AutoGenerateColumns = true; dgToDoItems.ItemsSource = toDoArray; } catch (WebException ex) { DisplayError(((HttpWebResponse)(ex.Response)).StatusCode); } catch (Exception ex) { MessageBox.Show((ex.Message)); } } HttpWebResponse GetResponseFromService(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; JavaScriptSerializer serializer = new JavaScriptSerializer(); if (item != null) { string content = serializer.Serialize(item); using (Stream stream = request.GetRequestStream()) { stream.Write(Encoding.UTF8.GetBytes(content), 0, content.Length); } } // Call the TodoListService return (HttpWebResponse)request.GetResponse(); } string GetAuthorizationHeader() { string authority = "https://dc1.corp.contoso.com/adfs"; string resourceURI = "http://corp.contoso.com/ToDoListService"; string clientID = "4BD0D3C2-97DF-408B-A6D4-A8FCF84030C0"; string clientReturnURI = "http://wpfclienturi/"; try { AuthenticationContext ac = new AuthenticationContext(authority, false); AuthenticationResult ar = ac.AcquireToken(resourceURI, clientID, new Uri(clientReturnURI)); string authHeader = ar.CreateAuthorizationHeader(); return authHeader; } catch (ActiveDirectoryAuthenticationException ex) { string message = ex.Message; if (ex.InnerException != null) { message += "InnerException : " + ex.InnerException.Message; } MessageBox.Show(message); } return null; } void DisplayError(HttpStatusCode statusCode) { switch (statusCode) { case HttpStatusCode.Unauthorized: { // An unauthorized error occurred, indicating the security token provided did not satisfy the service requirements // acquiring a new token may fix the issue. MessageBox.Show("You are not authorized to access the ToDoListService”); } break; default: MessageBox.Show("Sorry, accessing your ToDo list has hit a problem."); break; } } private void Button_Get(object sender, RoutedEventArgs e) { GetToDoList(); } private void Button_Add(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(txtToDoItem.Text)) { MessageBox.Show("Please enter a value for the to-do item name"); return; } string authorizationHeader = GetAuthorizationHeader(); if (authorizationHeader == null) { return; } try { ToDoItem item = new ToDoItem(); item.Title = txtToDoItem.Text; // Call the ToDoListService GetResponseFromService(authorizationHeader, item); txtToDoItem.Text = ""; GetToDoList(); } catch (WebException ex) { DisplayError(((HttpWebResponse)(ex.Response)).StatusCode); } catch (Exception ex) { MessageBox.Show((ex.Message)); } } } }
Now we will take a look at this code. The first thing we see is the following using directives had been added in addition to the default ones.
using System.Net.Http; using Microsoft.IdentityModel.Clients.ActiveDirectory; using System.Net; using System.Web.Script.Serialization; using System.Data; using System.IO;
Now we will look at the various parts of the code. To start, here is the code for the Button_Get click event. This code simply calls the GetToDoList method.
private void Button_Get(object sender, RoutedEventArgs e) { GetToDoList(); }
The GetToDoList method does the primary lifting with regard to retrieving the items that are stored in the TodoBag of our service. It acquires the token from AD FS, creates an HTTP GET request, attaches the token to the request. Once it receives the response from the ToDoListService it either deserializes the response if you were authorized to access the service and sets that to an array which is the source for our datagrid, or, it throws an error stating you were not authorized.
private async void GetToDoList() { string authorizationHeader = GetAuthorizationHeader(); if (authorizationHeader == null) { return; } try { HttpClient client = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44300/api/ToDoList"); request.Headers.TryAddWithoutValidation("Authorization", authorizationHeader); HttpResponseMessage response = await client.SendAsync(request); string responseString = await response.Content.ReadAsStringAsync(); JavaScriptSerializer serializer = new JavaScriptSerializer(); List<ToDoItem> toDoArray = serializer.Deserialize<List<ToDoItem>>(responseString); dgToDoItems.AutoGenerateColumns = true; dgToDoItems.ItemsSource = toDoArray; } catch (WebException ex) { DisplayError(((HttpWebResponse)(ex.Response)).StatusCode); } catch (Exception ex) { MessageBox.Show((ex.Message)); } }
The GetAuthorizationHeader method acquires the JWT token from AD FS and returns it as a string so that it can be added to an HTTP request.
string GetAuthorizationHeader() { string authority = "https://dc1.corp.contoso.com/adfs"; string resourceURI = "http://corp.contoso.com/ToDoListService"; string clientID = "4BD0D3C2-97DF-408B-A6D4-A8FCF84030C0"; string clientReturnURI = "http://wpfclienturi/"; try { AuthenticationContext ac = new AuthenticationContext(authority, false); AuthenticationResult ar = ac.AcquireToken(resourceURI, clientID, new Uri(clientReturnURI)); string authHeader = ar.CreateAuthorizationHeader(); return authHeader; } catch (ActiveDirectoryAuthenticationException ex) { string message = ex.Message; if (ex.InnerException != null) { message += "InnerException : " + ex.InnerException.Message; } MessageBox.Show(message); } return null; }
Now here is the code for the Button_Add click event. This code first verifies that the textbox is not empty. Then it sets a string equal to what is returned from calling GetAuthorizationHeader. Next it creates an instance of the ToDoItem based on the text in the textbox. It takes this item and the authorization header and passes it to the GetReposoneFromService which is returned in the form of a HTTPWebReposne. If the response is successful it clears the textbox and calls GettoDoList to display the new item that was just added.
private void Button_Add(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(txtToDoItem.Text)) { MessageBox.Show("Please enter a value for the to-do item name"); return; } string authorizationHeader = GetAuthorizationHeader(); if (authorizationHeader == null) { return; } try { ToDoItem item = new ToDoItem(); item.Title = txtToDoItem.Text; // Call the ToDoListService GetResponseFromService(authorizationHeader, item); txtToDoItem.Text = ""; GetToDoList(); } catch (WebException ex) { DisplayError(((HttpWebResponse)(ex.Response)).StatusCode); } catch (Exception ex) { MessageBox.Show((ex.Message)); } }
GetReponseFromService creates a new HTTP POST request. It takes the item and serializes it and adds it to the request along with the authorizationHeader. It returns the HTTP response.
HttpWebResponse GetResponseFromService(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; JavaScriptSerializer serializer = new JavaScriptSerializer(); if (item != null) { string content = serializer.Serialize(item); using (Stream stream = request.GetRequestStream()) { stream.Write(Encoding.UTF8.GetBytes(content), 0, content.Length); } } // Call the TodoListService return (HttpWebResponse)request.GetResponse(); }
Finally, the DisplayError method is used to display either an unauthorized message or it will display a generic error for any other errors.
void DisplayError(HttpStatusCode statusCode) { switch (statusCode) { case HttpStatusCode.Unauthorized: { // An unauthorized error occurred, indicating the security token provided did not satisfy the service requirements // acquiring a new token may fix the issue. MessageBox.Show("You are not authorized to access the ToDoListService”); } break; default: MessageBox.Show("Sorry, accessing your ToDo list has hit a problem."); break; } }
The last thing that we need to do is to add a ToDoItem class to our ToDoListClient_WPF 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 and then change the namespace to refelect the ToDoListClient_WPF. Either way, once this is done, our client is ready for testing.
Test the ToDoListClient_WPF
To test the ToDoListClient_WPF with the ToDoListService use the following procedure.
First, go to the ToDoListService Project and start it by pressing F5. Once this is running return to the ToDoListClient_WPF project.
In the ToDoListClient_WPF project, press F5 begin debugging. This will launch our ToDoListClient_WPF application.
Now, because we just started our ToDoListService, there is nothing in the TodoBag to be returned. So go ahead and enter “Walk the Dog” in the textbox and click Add. You will get prompted to sign in.
Sign-in with the credentials of a valid Active Directory user for the test domain we setup and you should see the item you entered and a checkbox next to it.