Encapsulate DICOM Documents - WinForms C#

This tutorial shows how to encapsulate and extract documents using the Encapsulated Document Information Object Definition (IOD) of a DICOM file in a C# WinForms application.

Overview  
Summary This tutorial covers how to encapsulate and extract documents using a DICOM dataset in a WinForms C# Application.
Completion Time 30 minutes
Visual Studio Project Download tutorial project (14 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 tutorial, before working on the Encapsulate DICOM Documents - WinForms C# tutorial.

Encapsulated Document Information Object

The specifications for the Encapsulated Document information objects are listed in Part 3 Section A.45 of the DICOM standard.

Two types of documents can be encapsulated in a DICOM dataset:

More details on the Encapsulated Document Module can be found in the Working With DICOM Encapsulated Documents topic.

Create the Project and Add LEADTOOLS References

In Visual Studio, create a new C# Windows WinForms project, and add the below necessary LEADTOOLS references.

The references needed depend upon the purpose of the project. References can be added by one or the other of the following two methods (but not both).

If using NuGet references, this tutorial requires the following NuGet package:

If using local DLL references, the following DLLs are needed.

The DLLs are located at <INSTALL_DIR>\LEADTOOLS22\Bin\Dotnet4\x64:

For a complete list of which DLL files are required for your application, refer to Files to be Included With Your Application.

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.

Create Form Controls

With the project created, the references added, and the license set, coding can begin.

In Solution Explorer, double-click Form1.cs to display it in the Designer. From the Toolbox, add the following controls:

Set the Enabled property for all TextBox, ComboBox, and Button controls to false so that the application enables them only when a DICOM DataSet is loaded.

Load DICOM and Check for Encapsulated Document

Add the using statements below to the top.

C#
// Using block at the top  
using Leadtools; 
using Leadtools.Dicom; 
using System; 
using System.IO; 
using System.Windows.Forms; 

Add the following global variables to the Form1 class.

C#
private DicomDataSet _dicomDS; 
private MemoryStream _docStream; 

In Solution Explorer, double-click Form1.cs to display it in the Designer. Click the Events icon in the Properties Window. Then, double-click the Load event to create an event handler if one does not already exist. This will bring up the code behind the form.

Add the following code inside the Form1_Load event handler to startup the DicomEngine object.

C#
private void Form1_Load(object sender, EventArgs e) 
{ 
   DicomEngine.Startup(); 
} 

Add a MenuStrip from the toolbox and add a DICOM menu to the form with a Load menu item. Set the text to &Load and leave the new item's name as loadToolStripMenuItem.

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

C#
private void loadToolStripMenuItem_Click(object sender, System.EventArgs e) 
{ 
   // Load DataSet 
   _dicomDS = new DicomDataSet(); 
   OpenFileDialog openDialog = new OpenFileDialog(); 
   openDialog.InitialDirectory = @"C:\LEADTOOLS22\Resources\Images\DICOM"; 
   openDialog.Filter = "DICOM dataset (.dcm)|*.dcm"; 
   if (openDialog.ShowDialog(this) == DialogResult.OK) 
   { 
      _dicomDS.Load(openDialog.FileName, DicomDataSetLoadFlags.None); 
      DicomElement encapsulatedDocElement = _dicomDS.FindFirstElement(null, DicomTag.EncapsulatedDocument, true); 
 
      // Enable Controls 
      foreach (Control child in Controls) 
         if (child.GetType() == typeof(TextBox) || child.GetType() == typeof(Button) || child.GetType() == typeof(ComboBox)) 
            child.Enabled = true; 
 
      if (encapsulatedDocElement != null) 
      { 
         DisableEditing(); 
         GetEncapsulatedDocInfo(encapsulatedDocElement); 
 
         // Enable extraction of encapsulated document 
         encapsulateButton.Enabled = false; 
         extractButton.Enabled = true; 
      } 
      else // No encapsulated document 
      { 
         EnableEditing(); 
 
         // Enable encapsulation of encapsulated document 
         encapsulateButton.Enabled = true; 
         extractButton.Enabled = false; 
      } 
   } 
} 

This shows an open file dialog to load a DICOM DataSet from disk. If the selected DataSet contains an encapsulated document, it displays the document information in the form, extracts the encapsulated document to a memory stream, and disables editing of the controls. The Extract Document button is enabled, as well.

If the loaded DataSet does not contain an encapsulated document, the application enables the controls to allow the input of information and the Encapsulate Document button is enabled instead.

The code for enabling and disabling edit of the control is as follows:

C#
private void EnableEditing() 
{ 
   foreach (Control child in Controls) 
   { 
      if (child.GetType() == typeof(TextBox)) 
      { 
         (child as TextBox).Clear(); 
         (child as TextBox).ReadOnly = false; 
      } 
      if (child.GetType() == typeof(ComboBox)) 
      { 
         (child as ComboBox).DropDownStyle = ComboBoxStyle.DropDownList; 
         (child as ComboBox).KeyDown -= Form1_KeyDown; 
         (child as ComboBox).SelectedIndex = -1; 
      } 
   } 
} 
 
private void DisableEditing() 
{ 
   foreach (Control child in Controls) 
   { 
      if (child.GetType() == typeof(TextBox)) 
         (child as TextBox).ReadOnly = true; 
      if (child.GetType() == typeof(ComboBox)) 
      { 
         (child as ComboBox).DropDownStyle = ComboBoxStyle.Simple; 
         (child as ComboBox).KeyDown += Form1_KeyDown; 
      } 
   } 
} 
 
private void Form1_KeyDown(object sender, KeyEventArgs e) 
{ 
      e.SuppressKeyPress = true; 
} 

Parse Encapsulated Document Info

The code below extracts the encapsulated document into a memory stream and displays the information about the document in the form controls.

C#
private void GetEncapsulatedDocInfo(DicomElement encapsulatedDocElement) 
{ 
   DicomEncapsulatedDocument encapsulatedDocument = new DicomEncapsulatedDocument(); 
   DicomCodeSequenceItem conceptNameCodeSequence = new DicomCodeSequenceItem(); 
   _docStream = new MemoryStream(); 
 
   // Get the encapsulated document 
   _dicomDS.GetEncapsulatedDocument(encapsulatedDocElement, false, _docStream, encapsulatedDocument, conceptNameCodeSequence); 
 
   // Document Type 
   switch (encapsulatedDocument.Type) 
   { 
      case DicomEncapsulatedDocumentType.Pdf: 
         typeComboBox.SelectedIndex = 0; // PDF 
         break; 
      case DicomEncapsulatedDocumentType.Cda: 
         typeComboBox.SelectedIndex = 1; // CDA 
         break; 
      default: 
         typeComboBox.Text = "Unknown"; // Unknown 
         break; 
   } 
 
   // Instance Number 
   instanceNumberTextBox.Text = encapsulatedDocument.InstanceNumber.ToString(); 
 
   // Content Date 
   contentDateTextBox.Text =  
      encapsulatedDocument.ContentDate.Month.ToString("D2") + 
      '-' + encapsulatedDocument.ContentDate.Day.ToString("D2") + 
      '-' + encapsulatedDocument.ContentDate.Year.ToString("D4"); 
   // Content Time 
   int contentDateTimeMicroseconds = encapsulatedDocument.ContentTime.Fractions / 1000; 
   contentTimeTextBox.Text =  
      encapsulatedDocument.ContentTime.Hours.ToString("D2") +  
      ':' + encapsulatedDocument.ContentTime.Minutes.ToString("D2") +  
      ':' + encapsulatedDocument.ContentTime.Seconds.ToString("D2") +  
      '.' + contentDateTimeMicroseconds.ToString("D3"); 
 
   // Acquisition Date 
   acquisitionDateTextBox.Text = 
      encapsulatedDocument.AcquisitionDateTime.Month.ToString("D2") + 
      '-' + encapsulatedDocument.AcquisitionDateTime.Day.ToString("D2") + 
      '-' + encapsulatedDocument.AcquisitionDateTime.Year.ToString("D4"); 
   // Acquisition Time 
   int acquisitionDateTimeMicroseconds = encapsulatedDocument.AcquisitionDateTime.Fractions / 1000; 
   acquisitionTimeTextBox.Text = 
      encapsulatedDocument.AcquisitionDateTime.Hours.ToString("D2") + 
      ':' + encapsulatedDocument.AcquisitionDateTime.Minutes.ToString("D2") + 
      ':' + encapsulatedDocument.AcquisitionDateTime.Seconds.ToString("D2") + 
      '.' + acquisitionDateTimeMicroseconds.ToString("D3"); 
 
   // Burned Annotations 
   if (encapsulatedDocument.BurnedInAnnotation == "NO")  
      burnedInAnnComboBox.SelectedIndex = 0;  
   else  
      burnedInAnnComboBox.SelectedIndex = 1; 
 
   // Document Title 
   documentTitleTextBox.Text = encapsulatedDocument.DocumentTitle; 
 
   // Verification Flag 
   if (encapsulatedDocument.VerificationFlag == "UNVERIFIED") 
      verificationFlagComboBox.SelectedIndex = 0; 
   else 
      verificationFlagComboBox.SelectedIndex = 1; 
 
   // HL7 Flag Identifier 
   HL7InstanceIdentifierTextBox.Text = encapsulatedDocument.HL7InstanceIdentifier; 
 
   // MIME Types 
   mimeTypesTextBox.Lines = encapsulatedDocument.GetListOfMimeTypes(); 
 
   // Concept Name Code Sequence 
   codingSchemeDesignatorTextBox.Text = conceptNameCodeSequence.CodingSchemeDesignator; 
   codeValueTextBox.Text = conceptNameCodeSequence.CodeValue; 
   codeMeaningTextBox.Text = conceptNameCodeSequence.CodeMeaning; 
} 

Extract Encapsulated Document

The code below prompts a user to save a copy of the encapsulated document to disk.

C#
private void extractButton_Click(object sender, EventArgs e) 
{ 
   SaveFileDialog saveDialog = new SaveFileDialog(); 
   saveDialog.InitialDirectory = @"C:\LEADTOOLS22\Resources\Images\"; 
   saveDialog.FileName = documentTitleTextBox.Text; 
   if (typeComboBox.Text == "PDF") 
      saveDialog.Filter = "Portable Document Format (.pdf)|*.pdf"; 
   else if (typeComboBox.Text == "CDA") 
      saveDialog.Filter = "Clinical Document Architecture format (.xml)|*.xml"; 
 
   if (saveDialog.ShowDialog(this) == DialogResult.OK) 
   { 
      try 
      { 
         using (FileStream fs = new FileStream(saveDialog.FileName, FileMode.Create)) 
            _docStream.WriteTo(fs); 
         MessageBox.Show("Encapsulated document saved"); 
      } 
      catch (Exception ex) 
      { 
         MessageBox.Show(ex.Message); 
      } 
   } 
} 

Encapsulate Document

The code below encapsulates a document from disk using the document information inputted into the forms.

C#
private void encapsulateButton_Click(object sender, EventArgs e) 
{ 
   OpenFileDialog openDocumentDialog = new OpenFileDialog(); 
   openDocumentDialog.InitialDirectory = @"C:\LEADTOOLS22\Resources\Images"; 
   if (typeComboBox.SelectedIndex == 0) // PDF 
      openDocumentDialog.Filter = "Portable Document Format (.pdf)|*.pdf"; 
   else if (typeComboBox.SelectedIndex == 1) // CDA 
      openDocumentDialog.Filter = "Clinical Document Architecture format (.xml)|*.xml"; 
   else 
   { 
      MessageBox.Show("Select Document Type first."); 
      return; 
   } 
   if (openDocumentDialog.ShowDialog(this) == DialogResult.OK) 
   { 
      DicomEncapsulatedDocument encapsulatedDocument = new DicomEncapsulatedDocument(); 
 
      // Document Type 
      switch (typeComboBox.SelectedIndex) 
      { 
         case 0: 
            encapsulatedDocument.Type = DicomEncapsulatedDocumentType.Pdf; 
            break; 
         case 1: 
            encapsulatedDocument.Type = DicomEncapsulatedDocumentType.Cda; 
            break; 
         default: 
            encapsulatedDocument.Type = DicomEncapsulatedDocumentType.Unknown; 
            break; 
      } 
 
      // Instance Number 
      try 
      { 
         encapsulatedDocument.InstanceNumber = int.Parse(instanceNumberTextBox.Text); 
      } 
      catch (Exception) 
      { 
         MessageBox.Show("Unable to parse Instance Number. Enter a valid value"); 
         return; 
      } 
 
      // Content Date and Time 
      try 
      { 
         string contentDateTimeString = contentDateTextBox.Text + ' ' + contentTimeTextBox.Text; 
         DateTime contentDateTime = DateTime.ParseExact(contentDateTimeString, "MM-dd-yyyy HH:mm:ss.fff", new System.Globalization.CultureInfo("en-US")); 
         encapsulatedDocument.ContentDate = new DicomDateValue(contentDateTime); 
         encapsulatedDocument.ContentTime = new DicomTimeValue(contentDateTime); 
      } 
      catch (FormatException) 
      { 
         MessageBox.Show("Unable to parse Content Date/Time. Be sure to follow the displayed format"); 
         return; 
      } 
 
      // Acquisition Date and Time 
      try 
      { 
         string acquisitionDateTimeString = acquisitionDateTextBox.Text + ' ' + acquisitionTimeTextBox.Text; 
         DateTime acquisitionDateTime = DateTime.ParseExact(acquisitionDateTimeString, "MM-dd-yyyy HH:mm:ss.fff", new System.Globalization.CultureInfo("en-US")); 
         encapsulatedDocument.AcquisitionDateTime = new DicomDateTimeValue(acquisitionDateTime); 
      } 
      catch (FormatException) 
      { 
         MessageBox.Show("Unable to parse Acquisition Date/Time. Be sure to follow the displayed format"); 
         return; 
      } 
 
      // Burned Annotations  
      if (burnedInAnnComboBox.SelectedIndex != -1) 
         encapsulatedDocument.BurnedInAnnotation = burnedInAnnComboBox.Text; 
      else 
      { 
         MessageBox.Show("Unable to parse Burned-In Annotation. Select a value from the drop-down list"); 
         return; 
      } 
 
      // Document Title 
      encapsulatedDocument.DocumentTitle = documentTitleTextBox.Text; 
 
      // Verification Flag 
      encapsulatedDocument.VerificationFlag = verificationFlagComboBox.Text; 
 
      // HL7 Instance Identifier 
      if (typeComboBox.Text == "CDA" && string.IsNullOrEmpty(HL7InstanceIdentifierTextBox.Text)) 
      { 
         MessageBox.Show("Unable to parse HL7 Instance Identifier. Enter a valid value"); 
         return; 
      } 
      else 
         encapsulatedDocument.HL7InstanceIdentifier = HL7InstanceIdentifierTextBox.Text; 
 
      // Mime types of the document 
      string[] mimeTypes = mimeTypesTextBox.Lines; 
      encapsulatedDocument.SetListOfMimeTypes(mimeTypes); 
 
      // Concept Name Code Sequence 
      DicomCodeSequenceItem conceptNameCodeSequence = new DicomCodeSequenceItem(); 
      conceptNameCodeSequence.CodingSchemeDesignator = codingSchemeDesignatorTextBox.Text; 
      conceptNameCodeSequence.CodeValue = codeValueTextBox.Text; 
      conceptNameCodeSequence.CodeMeaning = codeMeaningTextBox.Text; 
 
      // Encapsulate loaded document in loaded DICOM dataset 
      DicomElement element = _dicomDS.FindFirstElement(null, DicomTag.EncapsulatedDocument, true); 
      _dicomDS.SetEncapsulatedDocument(element, false, openDocumentDialog.FileName, encapsulatedDocument, conceptNameCodeSequence); 
 
      MessageBox.Show("The document " + openDocumentDialog.FileName + " has been encapsulated in the loaded dataset. Press OK to save the dataset"); 
      SaveFileDialog saveDicomDialog = new SaveFileDialog(); 
      saveDicomDialog.InitialDirectory = @"C:\LEADTOOLS22\Resources\Images\DICOM"; 
      saveDicomDialog.Filter = "DICOM dataset (.dcm)|*.dcm"; 
      if (saveDicomDialog.ShowDialog(this) == DialogResult.OK) 
         _dicomDS.Save(saveDicomDialog.FileName, DicomDataSetSaveFlags.None); 
      MessageBox.Show("DataSet save complete"); 
   } 
} 

Clear Button and Shutdown

The following code resets the controls and the loaded DICOM DataSet.

C#
private void clearButton_Click(object sender, EventArgs e) 
{ 
   // Clear loaded DataSet 
   if (_dicomDS != null) 
      _dicomDS = null; 
 
   // Reset form controls 
   foreach (Control child in Controls) 
   { 
      if (child.GetType() == typeof(TextBox)) 
      { 
         (child as TextBox).Clear(); 
         (child as TextBox).ReadOnly = false; 
      } 
      if (child.GetType() == typeof(ComboBox)) 
      { 
         (child as ComboBox).DropDownStyle = ComboBoxStyle.DropDownList; 
         (child as ComboBox).KeyDown -= Form1_KeyDown; 
         (child as ComboBox).SelectedIndex = -1; 
      } 
 
      if (child.GetType() == typeof(TextBox) || child.GetType() == typeof(Button) || child.GetType() == typeof(ComboBox)) 
         child.Enabled = false; 
   } 
} 

In Solution Explorer, double-click Form1.cs to display it in the Designer. Click the Events icon in the Properties Window. Then, double-click the FormClosing event.

Use the code below to shutdown the DICOM Engine and clear the memory stream.

C#
private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
{ 
   DicomEngine.Shutdown(); 
   _docStream.Close(); 
} 

Run the Project

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

If the steps are followed correctly, the application runs and allows the user to load a DICOM DataSet.

If the DataSet contains an encapsulated document, the application displays the information in the form and allows the document to be saved to disk. If the DataSet does not contain and encapsulated document, the user can encapsulate a document after providing the necessary information in the form.

Wrap-up

This tutorial showed how to use the DicomEncapsulatedDocument and the DicomDataSet classes to extract and write Private Data elements in DICOM DataSets.

See Also

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


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