Delphi Programming
Advertisement

In this tutorial, you’ll learn how to create a simple real-time multi-platform chat application in RAD Studio using Devart SecureBridge component called TScHubConnection, which implements the functionality of a SignalR client.

SignalR is a client/server communication protocol that adds real-time functionality to desktop and web apps. The server will be able to push content to connected clients as soon as it becomes available without waiting for the client to request data. SignalR client libraries have been implemented in many languages, but the Delphi community lacked such a library, so we at Devart decided to implement a SignalR client in SecureBridge, a security component library for Delphi.

Besides SecureBridge, Devart offers a wide range of software solutions such as reliable Delphi data access components for popular databases and cloud sources and advanced Delphi ORM tool EntityDAC.

Start by creating a new multi-device application. Our form will have a few panels, one at the top and one at the bottom. Also add a TMemo component, which is our main chat area where messages are displayed. Add a TSplitter component to the form, which will allow users to resize the panels at runtime. Add a TListBox to display a list of users to chat with.

Add TLabel components for the username and message fields, and a TEdit component, which will contain the name of the user that we’re currently chatting with.

Next, add a TEdit component that will hold our username. Add another TEdit component where we’ll type our message.

Add a button that connects to the server and a button for sending our messages. Finally, place the TScHubConnection component on the form. Now we can start coding the application logic. 

Switch to the unit view and declare public methods DoMessageReceipt, DoUserConnected, and DoUserDisconnected for receiving messages, establishing a connection and dropping the connection to the server. We’ll also declare private methods FillUserNameList, FillMessageList, Connect, Disconnect, and IsConnected, which are explained further.

type

  TfmChatForm = class(TForm)

    Panel1: TPanel;

    Panel2: TPanel;

    meChat: TMemo;

    Splitter1: TSplitter;

    lbUserName: TListBox;

    lbNickName: TLabel;

    lbMessage: TLabel;

    edNickName: TEdit;

    edMessage: TEdit;

    btConnect: TButton;

    btSend: TButton;

    SignalRClient: TScHubConnection;

    procedure FormDestroy(Sender: TObject);

    procedure btConnectClick(Sender: TObject);

    procedure btSendClick(Sender: TObject);

    procedure edNickNameKeyDown(Sender: TObject; var Key: Word;

      var KeyChar: Char; Shift: TShiftState);

    procedure edMessageKeyDown(Sender: TObject; var Key: Word;

      var KeyChar: Char; Shift: TShiftState);

  private

    { Private declarations }

    procedure FillUserNameList(const Values: array of Variant);

    procedure FillMessageList(const Values: array of Variant);

    procedure Connect;

    procedure Disconnect;

    function IsConnected: boolean;

  public

    { Public declarations }

    procedure DoMessageReceipt(Sender: TObject; const Values: array of Variant);

    procedure DoUserConnected(Sender: TObject; const Values: array of Variant);

    procedure DoUserDisconnected(Sender: TObject; const Values: array of Variant);

  end;

var

  fmChatForm: TfmChatForm;

Add System.JSON to the uses clause since we’re going to use JSON to send and receive messages from the server.

Implement the FillUserNameList method that parses a JSON array received from the server and updates the list of connected users in the TListBox component on the form.

procedure TfmChatForm.FillUserNameList(const Values: array of Variant);

var

  Value, UserNameValue: TJSONValue;

begin

  lbUserName.Items.Clear;

  Value := TJSONObject.ParseJSONValue(Values[0]);

  try

    if Value is TJSONArray then

      for UserNameValue in TJSONArray(Value) do

        lbUserName.Items.Add(UserNameValue.GetValue<string>());

  finally

    Value.Free;

  end;

end;


Now let’s write the code for the FillMessageList method. It parses a JSON array and updates the list of messages in the TMemo component.


procedure TfmChatForm.FillMessageList(const Values: array of Variant);

var

  Value, MessageValue: TJSONValue;

begin

  Value := TJSONObject.ParseJSONValue(Values[1]);

  try

    if Value is TJSONArray then

      for MessageValue in TJSONArray(Value) do

        if MessageValue is TJSONObject then

          meChat.Lines.Add(TJSONObject(MessageValue).Values['Item1'].Value + ' ' +

            TJSONObject(MessageValue).Values['Item2'].Value + ': ' + TJSONObject(MessageValue).Values['Item3'].Value);

  finally

    Value.Free;

  end;

end;

In the DoUserConnected event handler, we invoke the FillUserNameList and FillMessageList methods.

procedure TfmChatForm.FillUserNameList(const Values: array of Variant);

var

  Value, UserNameValue: TJSONValue;

begin

  lbUserName.Items.Clear;

  Value := TJSONObject.ParseJSONValue(Values[0]);

  try

    if Value is TJSONArray then

      for UserNameValue in TJSONArray(Value) do

        lbUserName.Items.Add(UserNameValue.GetValue<string>());

  finally

    Value.Free;

  end;

end;

The DoMessageReceipt event handler displays new messages in the chat area.

procedure TfmChatForm.DoMessageReceipt(Sender: TObject;

  const Values: array of Variant);

begin

  meChat.Lines.Add(Values[0] + ' ' + Values[1] + ': ' + Values[2])

end;

The DoUserDisconnected event handler invokes the FillUserNameList method to update the list of connected users when we disconnect from the server.


procedure TfmChatForm.DoUserDisconnected(Sender: TObject;

  const Values: array of Variant);

begin

  FillUserNameList(Values);

end;

The Connect method invokes the Start, Register, and Send methods of the TScHubConnection component. The Start method establishes a connection to the server, the Register method registers a handler that will be invoked when the hub method with the specified method name is invoked, and the Send method invokes a hub method on the server using the specified method name and arguments. The text property of the btConnect button changes to Disconnect after the connection has been successfully established.


procedure TfmChatForm.Connect;

begin

  SignalRClient.Register('Send', DoMessageReceipt, [varString, varString, varString]);

  SignalRClient.Register('Connected', DoUserConnected, [varString, varString]);

  SignalRClient.Register('Disconnected', DoUserDisconnected, [varString]);

  SignalRClient.Start;

  SignalRClient.Send('Connected', [edNickName.Text]);

  btConnect.Text := 'Disconnect';

  edNickName.Enabled := False;

  edMessage.Enabled := True;

  btSend.Enabled := True;

end;

The Disconnect method invokes the Send and Stop methods of SecureBridge. The Stop method drops the connection to the server. The text value of the btConnect button is changed to Connect and the username list is cleared.


procedure TfmChatForm.Disconnect;

begin

  SignalRClient.Send('Disconnected', [edNickName.Text]);

  SignalRClient.Stop;

  btConnect.Text := 'Connect';

  edNickName.Enabled  := True;

  edMessage.Enabled := False;

  btSend.Enabled := False;

  lbUserName.Items.Clear;

  meChat.Lines.Clear;

end;

The IsConnected method stores the state of the connection to the server.

function TfmChatForm.IsConnected: boolean;

begin

  Result := SignalRClient.State = hcsConnected;

end;


Implement the OnDestroy event handler that drops an active connection to the server before destroying the form when we close our application.


procedure TfmChatForm.FormDestroy(Sender: TObject);

begin

  if IsConnected then

    Disconnect;

end;


Now let’s implement the btConnectClick event handler that is triggered when we click the button. It checks whether the client is connected to the server: if the IsConnected function returns True, the client drops the connection to the server by executing the Disconnect method; in case of False, the event handler triggers the execution of the Connect method to establish a connection to the server. Also, it checks whether we’ve specified our username in the NickName field.

procedure TfmChatForm.btConnectClick(Sender: TObject);

begin

  if IsConnected then begin

    Disconnect;

    Exit;

  end;

  if edNickName.Text = then begin

    meChat.Lines.Add('Error: no user specified!');

  end;

  try

    Connect;

  except

    on E: Exception do

      meChat.Lines.Add('Error: ' + E.Message);

  end;

end;

Let’s write some code for the OnClick event listener for our btSend button to notify the server and other connected users that we’ve written a message. The handler invokes the Send method that sends our text message and username to the server and then clears the message field by assigning an empty string to it.

procedure TfmChatForm.btSendClick(Sender: TObject);

begin

  try

    SignalRClient.Send('Send', [edNickName.Text, edMessage.Text]);

  finally

    edMessage.Text := ;

  end;

end;

Finally, assign the server address to the URL property of the TScHubConnection component on the form.

Now you can build your app for various platforms. 

Signalr app.png

SecureBridge also offers Delphi clients and servers for SSH, SSL, HTTP/HTTPS, WebSocket, and other protocols. Visit the Devart site for more details on SecureBridge and other Delphi component libraries.

Advertisement