Introduction
Greetings all, I trust you're all doing well during this pandemic. Stay safe.
The goal of this article is to implement REST API operations in WPF.
Following is a list that we are going to focus on for UI decoration.
- How to call the REST API from C#. With respect to all 4 interfaces: Get, Post, Put & Delete
- Editable DataGrid in WPF
- Command binding for buttons & Property bindings in WPF
- Usage of BooleanToVisibility convertor in WPF
- Run multiple projects in .Net: the first one would be the REST API server's project & the is a Client - The WPF application
Note. This is part 3 of the Rest API implementation with WPF & EF.
If your focus is only on learning REST API's integration with WPF, then you can skip this below listing. However, I highly recommend going through this list of prerequisites.
- Building a DataAccessLayer
- Building a REST API Layer
- Data Bindings in WPF
- Relay Command in WPF
- INotifypropertychanged in MVVM
Now that you've done that, let's move on with our objective. We are going to focus on the middle layer from the following architecture. The presentation layer of course.
To do that, Right-click on solution => add new Project & then select WPF application.
I have divided this article into 3 subparts:
- The REST API integration in C#
- The ViewModel with all commands, properties & methods
- Last, the View displays the REST API operations.
The REST API integration in C#
We need to have a class for 4 interfaces, Get, Post, Put & Delete.
Right-click on the WPF project & click on the add new class option. Then name it WebAPI
First, we are going to add the Get method.
- Open connection using HttpClient class
- Add parameters as specified below
- Add the GetAsync method
- Wait till the client gets a response
- The return type is Task
class WebAPI
{
/// <summary>
/// Get employee details
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static Task<HttpResponseMessage> GetCall(string url)
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
string apiUrl = API_URIs.baseURI + url;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.Timeout = TimeSpan.FromSeconds(900);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(apiUrl);
response.Wait();
return response;
}
}
catch (Exception ex)
{
throw;
}
}
}
Second, is the Post method.
- The post method has one extra parameter -- T model (T is generic). It represents the data of new Employee which we are supposed to add.
- We are going to keep that parameter generic because this Post method could be used by the 'n' number of clients.
- Call PostAsJsonAsync method of HttpClient
/// <summary>
/// creates a new employee
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="model"></param>
/// <returns></returns>
public static Task<HttpResponseMessage> PostCall<T>(string url, T model) where T : class
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
string apiUrl = API_URIs.baseURI + url;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.Timeout = TimeSpan.FromSeconds(900);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PostAsJsonAsync(apiUrl, model);
response.Wait();
return response;
}
}
catch (Exception ex)
{
throw;
}
}
Third, is the Put method.
- The put method has 2 parameters as well. One is URI & the other is the model to be updated
- We are going to send the ID of an employee in URI itself.
- Call PutAsJsonAsync method of HttpClient.
/// <summary>
/// Updates employees details
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="model"></param>
/// <returns></returns>
public static Task<HttpResponseMessage> PutCall<T>(string url, T model) where T : class
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
string apiUrl = API_URIs.baseURI + url;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.Timeout = TimeSpan.FromSeconds(900);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PutAsJsonAsync(apiUrl, model);
response.Wait();
return response;
}
}
catch (Exception ex)
{
throw;
}
}
Finally, the Delete method.
- We are going to concatenate the ID of an employee to be deleted in URI itself. But it is a better practice to keep it separate.
- Call DeleteAsync method of HttpClient
/// <summary>
/// Delete employee's record
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static Task<HttpResponseMessage> DeleteCall(string url)
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
string apiUrl = API_URIs.baseURI + url;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.Timeout = TimeSpan.FromSeconds(900);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.DeleteAsync(apiUrl);
response.Wait();
return response;
}
}
catch (Exception ex)
{
throw;
}
}
Now that you've done that, we can move forward with the 2nd phase of this article, which is,
ViewModel
Let me give you a tour of MainWindowViewModel.
- Commands
- GetButtonClicked: Get Method call, Get all the employee's details
- PostButtonClicked: Creates a new employee
- PutButtonClicked: Updates an existing employee's record
- DeleteButtonClicked: Delete an existing employee's record
- ShowRegistrationForm: This shows the form for employee registration
- Constructor
- To initialize these commands
- Properties
- List of Employees: stores employee details that we fetch from the get command above.
- SelectedEmployee: this represents the selected record from Datagrid in view
- boolean IsLoadData: it sets true after we are finished fetching all the records.
- Registration form properties
- FirstName: gets a value from TextBox FirstName
- LastName: gets a value from TextBox LastName
- Gender: gets a value from TextBox Gender
- Salary: gets a value from TextBox Salary
- IsShowForm: sets true when "Button-Register Employee" click
- ShowPostMessage
class MainWindowViewModel
using A.Utilities;
using DataAccessLayer;
using Prism.Commands;
using Prism.Mvvm;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
namespace A
{
class MainWindowViewModel : BindableBase, INotifyPropertyChanged
{
#region Properties
private List<Employee> _employees;
public List<Employee> Employees
{
get { return _employees; }
set { SetProperty(ref _employees, value); }
}
private Employee _selectedEmployee;
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set { SetProperty(ref _selectedEmployee, value); }
}
private bool _isLoadData;
public bool IsLoadData
{
get { return _isLoadData; }
set { SetProperty(ref _isLoadData, value); }
}
private string _responseMessage = "Welcome to REST API Tutorials";
public string ResponseMessage
{
get { return _responseMessage; }
set { SetProperty(ref _responseMessage , value); }
}
#region [Create Employee Properties]
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { SetProperty(ref _firstName, value); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { SetProperty(ref _lastName, value); }
}
private string _gender;
public string Gender
{
get { return _gender; }
set { SetProperty(ref _gender, value); }
}
private int _salary;
public int Salary
{
get { return _salary; }
set { SetProperty(ref _salary, value); }
}
#endregion
private bool _isShowForm;
public bool IsShowForm
{
get { return _isShowForm; }
set { SetProperty(ref _isShowForm, value); }
}
private string _showPostMessage = "Fill the form to register an employee!";
public string ShowPostMessage
{
get { return _showPostMessage; }
set { SetProperty(ref _showPostMessage, value); }
}
#endregion
#region ICommands
public DelegateCommand GetButtonClicked { get; set; }
public DelegateCommand ShowRegistrationForm { get; set; }
public DelegateCommand PostButtonClick { get; set; }
public DelegateCommand<Employee> PutButtonClicked { get; set; }
public DelegateCommand<Employee> DeleteButtonClicked { get; set; }
#endregion
#region Constructor
/// <summary>
/// Initalize perperies & delegate commands
/// </summary>
public MainWindowViewModel()
{
GetButtonClicked = new DelegateCommand(GetEmployeeDetails);
PutButtonClicked = new DelegateCommand<Employee>(UpdateEmployeeDetails);
DeleteButtonClicked = new DelegateCommand<Employee>(DeleteEmployeeDetails);
PostButtonClick = new DelegateCommand(CreateNewEmployee);
ShowRegistrationForm = new DelegateCommand(RegisterEmployee);
}
#endregion
#region CRUD
/// <summary>
/// Make visible Regiter user form
/// </summary>
private void RegisterEmployee()
{
IsShowForm = true;
}
/// <summary>
/// Fetches employee details
/// </summary>
private void GetEmployeeDetails()
{
var employeeDetails = WebAPI.GetCall(API_URIs.employees);
if (employeeDetails.Result.StatusCode == System.Net.HttpStatusCode.OK)
{
Employees = employeeDetails.Result.Content.ReadAsAsync<List<Employee>>().Result;
IsLoadData = true;
}
}
/// <summary>
/// Adds new employee
/// </summary>
private void CreateNewEmployee()
{
Employee newEmployee = new Employee()
{
FirstName = FirstName,
LastName = LastName,
Gender = Gender,
Salary = Salary
};
var employeeDetails = WebAPI.PostCall(API_URIs.employees, newEmployee);
if (employeeDetails.Result.StatusCode == System.Net.HttpStatusCode.Created)
{
ShowPostMessage = newEmployee.FirstName + "'s details has successfully been added!";
}
else
{
ShowPostMessage = "Failed to update" + newEmployee.FirstName + "'s details.";
}
}
/// <summary>
/// Updates employee's record
/// </summary>
/// <param name="employee"></param>
private void UpdateEmployeeDetails(Employee employee)
{
var employeeDetails = WebAPI.PutCall(API_URIs.employees+ "?id="+employee.ID, employee);
if (employeeDetails.Result.StatusCode == System.Net.HttpStatusCode.OK)
{
ResponseMessage = employee.FirstName + "'s details has successfully been updated!";
}
else
{
ResponseMessage = "Failed to update"+ employee.FirstName + "'s details.";
}
}
/// <summary>
/// Deletes employee's record
/// </summary>
/// <param name="employee"></param>
private void DeleteEmployeeDetails(Employee employee)
{
var employeeDetails = WebAPI.DeleteCall(API_URIs.employees + "?id=" + employee.ID);
if (employeeDetails.Result.StatusCode == System.Net.HttpStatusCode.OK)
{
ResponseMessage = employee.FirstName + "'s details has successfully been deleted!";
}
else
{
ResponseMessage = "Failed to delete" + employee.FirstName + "'s details.";
}
}
#endregion
}
}
Finally, the WPF View is used to display the REST API operations.
- Get-Button: This button fetches the Employee's details
- DataGrid: It will display all the records fetched, But DataGrid will only be visible after clicking on Get-Button (BooleanToVisibilityConvertor)
- Update-Button: You can select a record from DataGrid & update the same in DataGrid as DataGrid is editable.
- Delete-Button: You can select & delete employee records.
- Register-Button: It is responsible for the visibility of the form below Update & Delete Buttons.
- Post-Button: creates a new employee as per data filled in the form
- 2 Messages: one will show if data is updated or deleted from DB & the below one will show if the employee's record is successfully created in DB or not.
After having all these fields updated, Our MainWindow will look like this.
MainWindow.xaml
<Window x:Class="A.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:ViewModel="clr-namespace:A"
Title="MainWindow" Height="600" Width="350">
<Window.Resources>
<ViewModel:MainWindowViewModel x:Key="VM" />
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Grid x:Name="MainGrid"
DataContext="{Binding Source={StaticResource VM}}"
HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="StackPanelGetPost"
Orientation="Horizontal"
HorizontalAlignment="Center"
Margin="0 10 0 0">
<Button x:Name="ButtonGet"
Command="{Binding GetButtonClicked}"
Height="20"
Width="120"
Content="GET"/>
<Button x:Name="ButtonPost"
Command="{Binding ShowRegistrationForm}"
Margin="10 0 0 0"
Height="20"
Width="120"
Content="Register Employee"
VerticalAlignment="Bottom"/>
</StackPanel>
<Grid x:Name="GridEmployeeDetails"
Visibility="{Binding IsLoadData, Converter={StaticResource BooleanToVisibilityConverter}}"
Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DataGrid x:Name="DataGridEmployee"
ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee}"
AutoGenerateColumns="False"
AlternatingRowBackground="LightBlue"
BorderBrush="Gray"
BorderThickness="5"
Background="LightGray"
Margin="0 10 0 0"
RowBackground="LightGray"
Width="310" >
<DataGrid.Columns>
<DataGridTextColumn x:Name="ColumnFirstName"
Header="First Name"
Binding="{Binding FirstName}"/>
<DataGridTextColumn x:Name="ColumnLastName"
Header="Last Name"
Binding="{Binding LastName}"/>
<DataGridTextColumn x:Name="ColumnGender"
Header="Gender"
Binding="{Binding Gender}"/>
<DataGridTextColumn x:Name="ColumnSalary"
Header="Salary"
Binding="{Binding Salary}"/>
</DataGrid.Columns>
</DataGrid>
<StackPanel x:Name="StackPanlePutDelete"
HorizontalAlignment="Center"
Margin="0 10 0 0"
Orientation="Horizontal"
Grid.Row="1">
<Button x:Name="ButtonPut"
Command="{Binding PutButtonClicked}"
CommandParameter="{Binding SelectedEmployee}"
Height="20"
Width="120"
Content="Update"/>
<Button x:Name="ButtonDelete"
Command="{Binding DeleteButtonClicked}"
CommandParameter="{Binding SelectedEmployee}"
Margin="10 0 0 0"
Height="20"
Width="120"
Content="Delete"/>
</StackPanel>
<TextBlock x:Name="TextBlockResponse"
Text="{Binding ResponseMessage}"
HorizontalAlignment="Center"
Margin="20 8 0 0"
Grid.Row="2" />
</Grid>
<Grid x:Name="GridCreateNewEmployee"
Visibility="{Binding IsShowForm, Converter={StaticResource BooleanToVisibilityConverter}}"
Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label x:Name="LabelUserFirstName"
Content="First Name:"
Margin="0 10 0 0"/>
<Label x:Name="LabelUserLastName"
Content="Last Name:"
Grid.Row="1"/>
<Label x:Name="LabelGender"
Content="Gender:"
Grid.Row="2"/>
<Label x:Name="LabelSalary"
Content="Salary:"
Grid.Row="3"/>
<TextBox x:Name="TextBoxUserFirstName"
Text="{Binding FirstName}"
Height="20"
Width="150"
Grid.Column="1"/>
<TextBox x:Name="TextBoxUserLastName"
Text="{Binding LastName}"
Height="20"
Width="150"
Grid.Row="1"
Grid.Column="1"/>
<TextBox x:Name="TextBoxGender"
Text="{Binding Gender}"
Height="20"
Width="150"
Grid.Row="2"
Grid.Column="1"/>
<TextBox x:Name="TextBoxSalary"
Text="{Binding Salary}"
Height="20"
Width="150"
Grid.Column="1"
Grid.Row="3"/>
<Button x:Name="ButtonAdd"
Height="20"
Width="100"
Content="POST"
HorizontalAlignment="Center"
Margin="20 10 0 0"
Now run that beautiful piece of code, and you will be able to perform the actions the same as in the following gif.
Note. Watch the complete gif to come across all the operations.
That just looks delicious. Everything has been added in perfect amounts & it is working as smoothly as butter on a hot pan & believe me, the final product does taste good.
So, folks, This is how you call a REST API in your C# backed project.
Conclusion
- We can have 4 very generic methods, each representing interfaces of a REST API. We achieved this by using generic in our code
- How to create a basic UI in WPF for these operations
- How to hide/unhide forms or a particular section of the screen using BooleanToVisibilityConvertor
I will leave you here to do further R & D. Find the source code here for your reference. Give it a shot!
I also appreciate the patience of the audience who has come along for all 3 parts to learn this. Kudos.
If you have all the 3 projects running under 1 solution, then you have to select 2 major projects as the startup project. Go to the solution properties and do as follows.
If you wish to talk, feel free to meet me @