Create a Simple PACS Client With a Medical Viewer - WinForms C#

This tutorial shows how to configure a simple PACS client application that can establish a connection to a PACS Server, query for stored DICOM studies and series, then retrieve a dataset and display it in a medical viewer control.

Overview  
Summary This tutorial covers how to create a simple PACS client in a WinForms C# Application.
Completion Time 30 minutes
Visual Studio Project Download tutorial project (16 KB)
Platform Windows WinForms C# Application
IDE Visual Studio 2019
Development License Download LEADTOOLS

Required Knowledge

Get familiar with the basic steps of creating a project by reviewing the Add References and Set a License and Load and Display a DICOM Image in the Medical Viewer tutorials, Before working on the Create a Simple PACS Client With a Medical Viewer - WinForms C# tutorial.

Create the Project and Add the LEADTOOLS References

Start with a copy of the project created in the Load and Display a DICOM Image in the Medical Viewer tutorial. If you don't have that project, follow the steps in that tutorial to create it.

Ensure the project has the necessary LEADTOOLS references mentioned in the original tutorial.

If NuGet references are used, this tutorial requires the following additional NuGet package:

If local DLL references are used, the following additional DLLs are needed. The DLLs are located at <INSTALL_DIR>\LEADTOOLS22\Bin\Dotnet4\x64:

For a complete list of which Codec DLLs are required for specific formats, refer to File Format Support.

Set the License File

The License unlocks the features needed for the project. It must be set before any toolkit function is called. For details including tutorials for different platforms, refer to Setting a Runtime License.

There are two types of runtime licenses:

Note

Adding LEADTOOLS NuGet and local references and setting a license are covered in more detail in the Add References and Set a License tutorial.

Add the Retrieve Image Menu Item and Code

Now that the LEADTOOLS references have been added to the project and the license has been set, coding can begin.

Open Form1.cs in the Designer, then add a PACS menu with a Retrieve Image menu item. Leave the new item's name as retrieveImageToolStripMenuItem.

Double-click the *Retrieve Image menu item to edit its event handler. Add the following code in it:

C#
private void retrieveImageToolStripMenuItem_Click(object sender, EventArgs e) 
{ 
   PACSQueryDialog dialog = new PACSQueryDialog(); 
 
   DialogResult dr = dialog.ShowDialog(this); 
 
   if (dr == DialogResult.OK) 
   { 
      try 
      { 
         _medicalViewer.Cells.Clear(); 
 
         foreach (DicomDataSet dataSet in dialog.retrievedDatasets) 
         { 
            _cell = null; 
            _dataSet = dataSet; 
 
            InitializeMultiCell(); 
 
            if (_cell != null) 
            { 
               SetTags(); 
 
               _medicalViewer.Cells.Add(_cell); 
            } 
         } 
      } 
      catch (Exception ex) 
      { 
         MessageBox.Show(ex.ToString()); 
      } 
   } 
} 

Create the PACS Query Dialog

Right-click on the project and add a new form named PACSQueryDialog. Open PACSQueryDialog.cs in the designer and add the following controls:

Add the PACS Query Code

Configure PACS Connection

Right-click on PACSQueryDialog.cs in the Solution Explorer and select View Code to display the code behind the form.

Add the following code to the using block:

C#
// Using block at the top  
using System; 
using System.Collections.Generic; 
using System.Net; 
using System.Windows.Forms; 
using Leadtools.Dicom; 
using Leadtools.Dicom.Scu; 
using Leadtools.Dicom.Scu.Common; 

Add the code below to the dialog's global variables:

C#
// Add these global variables  
public List<DicomDataSet> retrievedDatasets = new List<DicomDataSet>(); 
 
private QueryRetrieveScu clientSCU = new QueryRetrieveScu(); 
private DicomScp sourceSCP = new DicomScp(); 
private FindQuery query = new FindQuery(); 
// Client Connection Info 
private string clientAE = @""; 
private string clientIP = @""; 
private string clientPort = @""; 
// Server Connection Info 
private string pacsServerAE = @""; 
private string pacsServerIP = @""; 
private string pacsServerPort = @""; 

The public collection, retrievedDatasets, will hold the retrieved DICOM datasets that will then be accessible for display in the medical viewer control.

Valid PACS Client and Server information need to be filled for the application to function successfully.

Find and Retrieve Studies from the Server

In the Solution Explorer, Go to the PACSQueryDialog.cs form's properties and double-click the Load event to create a PACSQueryDialog_Load event handler for the form. This will bring up the code behind the form.

Add the following code to initialize the DicomNet, sourceSCP, and clientSCU classes then use clientSCU.Find() to retrieve all DICOM studies found in the Server and add them to the ListView control.

C#
private void PACSQueryDialog_Load(object sender, EventArgs e) 
{ 
   DicomNet.Startup(); 
 
   // Initialize the SCU 
   try 
   { 
      clientSCU.AETitle = clientAE; 
      clientSCU.HostAddress = IPAddress.Parse(clientIP); 
      clientSCU.HostPort = Convert.ToInt32(clientPort); 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Exception initializing the QueryRetrieveScu Class", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return; 
   } 
 
   try 
   { 
      clientSCU.MatchStudy += new MatchStudyDelegate(clientSCU_MatchStudy); 
      clientSCU.MatchSeries += new MatchSeriesDelegate(clientSCU_MatchSeries); 
      clientSCU.AfterCFind += new AfterCFindDelegate(clientSCU_AfterCFind); 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Exception adding event handlers to the QueryRetrieveScu Class", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return; 
   } 
 
   // Initialize the SCP 
   try 
   { 
      sourceSCP.AETitle = pacsServerAE; 
      sourceSCP.PeerAddress = IPAddress.Parse(pacsServerIP); 
      sourceSCP.Port = Convert.ToInt32(pacsServerPort); 
      sourceSCP.Timeout = 60; 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Exception setting up SCP", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return; 
   } 
 
   // Find Studies and add them to ListView 
   try 
   { 
      listViewStudies.BeginUpdate(); 
 
      query.QueryLevel = QueryLevel.Study; 
      clientSCU.Find(sourceSCP, query); 
 
      listViewStudies.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize); 
      listViewStudies.EndUpdate(); 
      listViewStudies.Items[0].Selected = true; 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Exception from QueryRetrieveScu.Move()", MessageBoxButtons.OK, MessageBoxIcon.Error); 
   } 
} 
 
// Add the information from each study to the ListView control 
void clientSCU_MatchStudy(object sender, MatchEventArgs<Study> e) 
{ 
   ListViewItem item; 
 
   item = listViewStudies.Items.Add(e.Info.Patient.Name.ToString()); 
   item.SubItems.Add(e.Info.Patient.Id.ToString()); 
   item.SubItems.Add(e.Info.AccessionNumber.ToString()); 
   item.SubItems.Add(e.Info.Date.ToString()); 
   item.SubItems.Add(e.Info.ReferringPhysiciansName.ToString()); 
   item.SubItems.Add(e.Info.Description.ToString()); 
   item.SubItems.Add(e.Info.InstanceUID.ToString()); 
} 

Find and Retrieve Series from the Server

In the Solution Explorer, open the designer for the PACSQueryDialog.cs form. Go to the listViewStudies ListView control properties and double-click the SelectedIndexChanged event to create a listViewStudies_SelectedIndexChanged event handler for the form. This will bring up the code behind the form.

Add the following code so that the series for the selected study are retrieved and added to the series ListView control.

C#
private void listViewStudies_SelectedIndexChanged(object sender, EventArgs e) 
{ 
   if (listViewStudies.SelectedItems.Count == 0) 
      return; 
 
   listViewSeries.BeginUpdate(); 
   listViewSeries.Items.Clear(); 
 
   query.QueryLevel = QueryLevel.Series; 
   query.StudyInstanceUID = listViewStudies.SelectedItems[0].SubItems[6].Text; 
   clientSCU.Find(sourceSCP, query); 
 
   listViewSeries.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize); 
   listViewSeries.EndUpdate(); 
   listViewSeries.Items[0].Selected = true; 
} 
 
void clientSCU_MatchSeries(object sender, MatchEventArgs<Series> e) 
{ 
   ListViewItem item; 
 
   item = listViewSeries.Items.Add(e.Info.Date.ToString()); 
   item.SubItems.Add(e.Info.Number.ToString()); 
   item.SubItems.Add(e.Info.Description.ToString()); 
   item.SubItems.Add(e.Info.Modality.ToString()); 
   item.SubItems.Add(e.Info.NumberOfRelatedInstances.ToString()); 
   item.SubItems.Add(e.Info.InstanceUID.ToString()); 
} 
 
void clientSCU_AfterCFind(object sender, AfterCFindEventArgs e) 
{ 
   if (e.Status != DicomCommandStatusType.Success) 
   { 
      // If there is a problem, print the additional status elements  
      string statusAllString = e.StatusAll.ToString(); 
      Console.WriteLine(statusAllString); 
   } 
} 

Retrieve DICOM DataSet for the Selected Study

Double-click on the Display Series Button, to create a buttonDisplaySeries_Click event handler and to bring up the code behind the form.

Add the code below to move the dataset for the selected series from the server to the client and make it available for the medical viewer control.

C#
private void buttonDisplaySeries_Click(object sender, EventArgs e) 
{ 
   try 
   { 
      clientSCU.Moved += new MovedDelegate(retrieveStudy_Moved); 
      clientSCU.AfterCMove += new AfterCMoveDelegate(retrieveStudy_AfterCMove); 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Exception adding SCU C-MOVE event handlers", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return; 
   } 
 
   try 
   { 
      string studyUID, seriesUID; 
      if (listViewStudies.SelectedItems.Count == 0 && listViewSeries.SelectedItems.Count == 0) 
         return; 
 
      studyUID = listViewStudies.SelectedItems[0].SubItems[6].Text; 
      seriesUID = listViewSeries.SelectedItems[0].SubItems[5].Text; 
 
      if (studyUID != string.Empty && seriesUID != string.Empty) 
         clientSCU.Move(sourceSCP, string.Empty, studyUID, seriesUID); 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Exception from QueryRetrieveScu.Move()", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return; 
   } 
 
   // Done 
   this.DialogResult = DialogResult.OK; 
   this.Close(); 
} 
 
private void retrieveStudy_Moved(object sender, MovedEventArgs movedEventArgs) 
{ 
   retrievedDatasets.Add(movedEventArgs.Dataset); 
} 
 
private void retrieveStudy_AfterCMove(object sender, AfterCMoveEventArgs e) 
{ 
   if (e.Status != DicomCommandStatusType.Success) 
   { 
      // If there is a problem, print the additional status elements  
      string statusAllString = e.StatusAll.ToString(); 
      Console.WriteLine(statusAllString); 
   } 
} 

In the Solution Explorer, Go to the PACSQueryDialog.cs form's properties and double-click the FormClosing event to create a PACSQueryDialog_FormClosing event handler for the form. This will bring up the code behind the form.

Call DicomNet.Shutdown() in the event handler:

C#
private void PACSQueryDialog_FormClosing(object sender, FormClosingEventArgs e) 
{ 
   try 
   { 
      DicomNet.Shutdown(); 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Exception from DicomNet.Shutdown()", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return; 
   } 
} 

Run the Project

Run the project by pressing F5, or by selecting Debug -> Start Debugging.

If the steps were followed correctly, the application runs and allows the user to find and retrieve DICOM studies and series from a PACS server. The user can then retrieve the DICOM dataset associated to a selected series and display it on the medical viewer control.

Note: For the application to function properly, the PACS Server should be configured to allow connections from the Client AE, IP, and port defined in this tutorial.

This LEADTOOLS Dicom Server demo (found here: <INSTALL_DIR>\LEADTOOLS22\Shortcuts\PACS\.NET Framework Class Libraries\PACS (Low Level)\Server) can be used with this client. The client connection can be configured from the Users tab in this demo after clicking Add User.

Wrap-up

This tutorial showed how to add the necessary references to create a PACS client application that can connect, find, and move DICOM datasets for display in a MedicalViewer WinForms control. In addition, it showed how to use the QueryRetrieveScu, DicomScp, and FindQuery objects.

See Also

Help Version 22.0.2021.10.18
Products | Support | Contact Us | Intellectual Property Notices
© 1991-2021 LEAD Technologies, Inc. All Rights Reserved.

Products | Support | Contact Us | Intellectual Property Notices
© 1991-2021 LEAD Technologies, Inc. All Rights Reserved.