Client-Server Communication Tutorial

Client-Server Communication Tutorial

This page is a continuation of the Custom Devices tutorial.

Visual Studio Project:

Connexion (and Gateway) include a mechanism for your device user interface to invoke methods in your Device class. This is useful in many scenarios where you need to exchange information between your user interface and server-side device.

In the previous tutorial, we created a device which extracted HL7v2 information and inserted it into a database. We’re going to add functionality to this device in order to report the number of records in our Patient table when the user clicks a button within the device UI.

image-20250824-192203.png
Our device at the end of the last tutorial

The first step in enabling communications is to define an interface containing the device method signature. For our tutorial, we’ll define a single method that returns an integer. This code can be put into a new class file, or, appended to the existing Device file.

public interface IDatabaseInformation { Task<int> GetPatientCountAsync(); }

Next, we need to implement this interface within our Device (update the class definition per below). Remember, our device is running on the Connexion host machine:

public class HL7FilerTutorial : BaseDevice<HL7FilerTutorialConfiguration>, IDatabaseInformation { ... }

Implement the interface method to return the number of Patient records in the database.

public async Task<int> GetPatientCountAsync() { var builder = Configuration.DatabaseConnection.GetProviderConnectionStringBuilder(); using (var connection = new Microsoft.Data.SqlClient.SqlConnection(builder.ConnectionString)) { await connection.OpenAsync(); using (var command = connection.CreateCommand()) { command.CommandText = "Select Count(*) From Patient"; return (int)await command.ExecuteScalarAsync(); } } }

Next, we need to update our user interface to call this server-side method and return the results to the user. First, update the Xaml to include a new button.

<UserControl x:Class="HL7FilerTutorial.HL7FilerTutorialUI" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:core="clr-namespace:Connexion.Core;assembly=Connexion.Core" xmlns:hl7filertutorial="clr-namespace:HL7FilerTutorial" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" d:DataContext="{d:DesignInstance hl7filertutorial:HL7FilerTutorialUiViewModel}" Padding="6"> <Grid Margin="0,6,0,0"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <core:DatabaseConnectionControl ConnectionParameters="{Binding Configuration.DatabaseConnection}" DeviceUIParameters="{Binding DeviceUiParams}" Margin="10"/> <Button Grid.Row="1" Margin="10" Content="Get Record Count" VerticalAlignment="Top" HorizontalAlignment="Left" Command="{Binding GetRecordCountCommand}"/> </Grid> </UserControl>

Next, we need to update our DeviceUiViewModel class. This is the class that the Xaml file is data bound to.

Connexion recommends you use the MVVM pattern for your user interfaces. There are many great tutorials out there explaining this pattern.

using Connexion.Core; using System; using System.Threading.Tasks; using System.Windows.Input; namespace HL7FilerTutorial { public class HL7FilerTutorialUiViewModel : ViewModelBase { public HL7FilerTutorialUiViewModel(HL7FilerTutorialConfiguration config, IDeviceUIParams deviceUiParams) { Configuration = config; DeviceUiParams = deviceUiParams; DatabaseInformationProxy = deviceUiParams.GetServerDeviceProxy<IDatabaseInformation>(); } public HL7FilerTutorialConfiguration Configuration { get; } public IDeviceUIParams DeviceUiParams { get; } public IDatabaseInformation DatabaseInformationProxy { get; } private RelayCommandAsync m_GetRecordCountCommand; public ICommand GetRecordCountCommand { get { return m_GetRecordCountCommand ?? (m_GetRecordCountCommand = new RelayCommandAsync(GetRecordCountAsync)); } } private async Task GetRecordCountAsync() { try { var patientCount = await DatabaseInformationProxy.GetPatientCountAsync(); MessageDialog.Show($"There are {patientCount} patient record(s) in the patient table.", "Record Count", false, MessageDialogStyle.Ok); } catch (Exception ex) { DisplayErrorDialog(ex); } } } }

Note line 15, where the proxy is created to the server-side Device class. At the bottom of this class we create a Command which connects our UI button with the GetRecordCountAsync() method.

Update the version number in your AssemblyInfo class and re-compile the project. Re-importing your device should update the UI to look similar to this:

image-20250824-195011.png

Clicking the Get Record Count button in the bottom left should display a message box with the number of records in your table.

image-20250824-195118.png

Most devices will update the user interface controls and state instead of showing a message box. For example, it’s common to have a Diagnostics section on a device which queries the server-side state and displays it in a textblock on the user interface.

image-20250824-195849.png