Load DICOMDIR using a Series Browser Dialog - WinForms C#

This tutorial shows how to load and display 2D and 3D images from a DICOM file-set's DICOMDIR through a series browser dialog in the LEADTOOLS MedicalViewer WinForms control.

Overview  
Summary This tutorial shows how to load medical images from the DICOMDIR file in a DICOM file-set using a series browser dialog in a WinForms C# Application.
Completion Time 45 minutes
Visual Studio Project Download tutorial project (22 KB)
Platform Windows WinForms C# Application
IDE Visual Studio 2022
Development License Download LEADTOOLS

Required Knowledge

Get familiar with the basic steps of creating a project and displaying a DICOM image 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 Load DICOMDIR using a Series Browser Dialog - 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 the project is not available, follow the steps in that tutorial to create it.

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:

Create the SeriesBrowser Dialog

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

In the Solution Explorer, right-click the project and select Add -> Add New Item... then choose to add a Form (Windows Form) called "SeriesBrowser". From the Toolbox, add a Panel that will hold the dialog's buttons. Set the Dock property to Bottom.

Add a SplitContainer and set the Orientation property to Horizontal.

Add a DataGridView control to each of the panels. Set the name for the top DataGridView control to studiesDataGridView and the bottom control to seriesDataGridView. Set the following properties for each of the DataGridView controls:

Modify the Columns property for the studiesDataGridView control and add the following columns:

Modify the Columns property for the seriesDataGridView control and add the following columns:

Add the following buttons to the Panel control:

Load DICOMDIR Code

With the dialog form created, right-click the SeriesBrowser.cs file in the Solution Explorer and select View Code.

Modify the Using block at the top of the file to be as follows:

C#
using System; 
using System.Collections.Generic; 
using System.Windows.Forms; 
using Leadtools; 
using Leadtools.Dicom; 
using Leadtools.MedicalViewer; 

Double-click the Add DICOMDIR button to add the onClick event handler. Add the following code to show an Open File Dialog to select the DICOMDIR file from a DICOM file-set then load the Studies and Series information contained for display in the DataGridView controls.

C#
private void btnAddDICOMDIR_Click(object sender, EventArgs e) 
{ 
   DicomDataSet dicomdirDataSet = new DicomDataSet(); 
 
   OpenFileDialog dialog = new OpenFileDialog(); 
   dialog.Filter = "DICOMDIR Files|DICOMDIR|All files (*.*)|*.*"; 
   dialog.FilterIndex = 1; 
   try 
   { 
      if (dialog.ShowDialog() == DialogResult.OK) 
      { 
         if (dialog.FileName == null) 
            return; 
 
         dicomdirDataSet.Load(dialog.FileName, DicomDataSetLoadFlags.None); 
 
         // Add the DICOMDIR study to the study Grid. 
         AddStudyRows(dicomdirDataSet, dialog.FileName); 
 
         // Select last loaded study 
         int lastRowIndex = studiesDataGridView.Rows.Count - 1; 
         studiesDataGridView.CurrentCell = studiesDataGridView.Rows[lastRowIndex].Cells[0]; 
         ViewSeriesRowsForThisStudy(lastRowIndex); 
 
         // Show and enable Buttons 
         btnLoad.Visible = true; 
         btnClear.Visible = true; 
 
         dicomdirDataSet.Dispose(); 
      } 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, dialog.FileName, MessageBoxButtons.OK); 
   } 
} 

Add Study Rows

Add a new function named AddStudyRows(DicomDataSet dicomdirDataSet, string dicomdirFileName). This method will be called inside the btnAddDICOMDIR_Click event handler, as shown above. Add the code below to fill in the rows' information for the studiesDataGridView control for every patient element in the DICOM file-set.

C#
private void AddStudyRows(DicomDataSet dicomdirDataSet, string dicomdirFileName) 
{ 
   // Get Patient Dicom Element 
   DicomElement patientElement = dicomdirDataSet.GetFirstKey(null, true); 
   if (patientElement == null) 
   { 
      MessageBox.Show("Patient Element is Null", "Invalid DICOMDIR", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); 
      return; 
   } 
 
   try 
   { 
      DicomElement studyElement; 
      DicomElement patientNameInformation; 
      DicomElement patientIdInformation; 
      DicomElement patientTagInformation; 
 
      string[] row = new string[8]; 
      string patientName = ""; 
      string patientId = ""; 
 
      // All the information that will be extracted from the studyElement. 
      // This method will extract these tags and 2 others (Patient Name and Patient ID). 
      long[] tags = 
      { 
         DicomTag.StudyDescription, 
         DicomTag.AccessionNumber, 
         DicomTag.StudyDate, 
         DicomTag.ReferringPhysicianName, 
         DicomTag.StudyInstanceUID 
      }; 
 
      // Patient Name 
      patientNameInformation = dicomdirDataSet.FindFirstElement(null, DicomTag.PatientName, false); 
      if (patientNameInformation != null) 
         patientName = dicomdirDataSet.GetConvertValue(patientNameInformation); 
 
      // Patient ID 
      patientIdInformation = dicomdirDataSet.FindFirstElement(null, DicomTag.PatientID, false); 
      if (patientIdInformation != null) 
         patientId = dicomdirDataSet.GetConvertValue(patientIdInformation); 
 
      while (patientNameInformation != null) 
      { 
         // Get the first Study 
         studyElement = dicomdirDataSet.GetChildKey(patientElement); 
         studyElement = dicomdirDataSet.GetChildElement(studyElement, true); 
 
         while (studyElement != null) 
         { 
            row[0] = patientName; 
            row[1] = patientId; 
 
            // Rest of Tags 
            for (int i = 2; i < 7; i++) 
            { 
               patientTagInformation = dicomdirDataSet.FindFirstElement(studyElement, tags[i - 2], true); 
 
               if (patientTagInformation != null) 
                  row[i] = dicomdirDataSet.GetConvertValue(patientTagInformation); 
            } 
 
            row[7] = dicomdirFileName; 
 
            bool alreadyExists = false; 
            foreach (DataGridViewRow studyRow in studiesDataGridView.Rows) 
               if (studyRow.Cells[6].Value.ToString().Trim().Contains(row[6].Trim())) 
                  alreadyExists = true; 
 
            // After retrieving all required study information, add them to the study Grid 
            if (!alreadyExists) 
               studiesDataGridView.Rows.Add(row); 
 
            // Add series rows for all studies in the DICOMDIR 
            AddSeriesRows(dicomdirDataSet, studyElement, row[6], dicomdirFileName); 
 
            // Go to the next study element 
            studyElement = dicomdirDataSet.GetNextKey(studyElement, true); 
            studyElement = dicomdirDataSet.GetChildElement(studyElement, true); 
         } 
 
         // Go to the next patient element 
 
         // Next Patient Name 
         patientNameInformation = dicomdirDataSet.FindNextElement(patientNameInformation, false); 
         if (patientNameInformation != null) 
            patientName = dicomdirDataSet.GetConvertValue(patientNameInformation); 
 
         // Next Patient ID 
         patientIdInformation = dicomdirDataSet.FindNextElement(patientIdInformation, false); 
         if (patientIdInformation != null) 
            patientId = dicomdirDataSet.GetConvertValue(patientIdInformation); 
 
         patientElement = dicomdirDataSet.GetNextKey(patientElement, true); 
      } 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Error Adding Study", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); 
   } 
} 

Add Series Rows

Add a new function named AddSeriesRows(DicomDataSet dicomdirDataSet, DicomElement studyElement, string studyID, string dicomDirFileName) and call it inside the AddStudyRows method, as shown above. Add the code below to add and display the associated series from the DICOM file-set, for each study loaded.

C#
private void AddSeriesRows(DicomDataSet dicomdirDataSet, DicomElement studyElement, string studyID, string dicomDirFileName) 
{ 
   try 
   { 
      int i; 
      bool hasImage = false; 
      Object[] row = new Object[9]; 
      // All the information that will be extracted from the series element. 
      // This method will extract these tags and 2 others (studyID and frame count). 
      long[] tags = 
      { 
         DicomTag.Modality, 
         DicomTag.SeriesDate, 
         DicomTag.SeriesNumber, 
         DicomTag.OrganExposed, 
         DicomTag.SeriesDescription, 
         DicomTag.NumberOfSeriesRelatedInstances, 
         DicomTag.SeriesInstanceUID 
      }; 
 
      DicomElement seriesDicomElement; 
      DicomElement seriesInformation; 
 
      seriesDicomElement = dicomdirDataSet.GetChildKey(studyElement); 
      seriesDicomElement = dicomdirDataSet.GetChildElement(seriesDicomElement, true); 
 
      while (seriesDicomElement != null) 
      { 
         for (i = 0; i < tags.Length; i++) 
         { 
            seriesInformation = dicomdirDataSet.FindFirstElement(seriesDicomElement, tags[i], true); 
 
            if (seriesInformation != null) 
               row[i] = dicomdirDataSet.GetConvertValue(seriesInformation); 
         } 
 
         row[i] = studyID; // row[8] 
 
         // Get Frame Count 
         DicomElement seriesElement; 
         int frameCount = 0; 
         seriesElement = dicomdirDataSet.GetChildKey(seriesDicomElement); 
         while (seriesElement != null) 
         { 
            frameCount++; 
            seriesElement = dicomdirDataSet.GetNextKey(seriesElement, true); 
         } 
         row[i + 1] = frameCount; // row[9] 
 
         // Check to see if series contains images 
         DicomElement imageElement; 
         int index = 0; 
         int thumbnailIndex = frameCount / 2; 
 
         imageElement = dicomdirDataSet.GetChildKey(seriesDicomElement); 
         imageElement = dicomdirDataSet.GetChildElement(imageElement, true); 
 
         while (imageElement != null) 
         { 
            if (index >= thumbnailIndex) 
            { 
               imageElement = dicomdirDataSet.FindFirstElement(imageElement, DicomTag.ReferencedFileID, true); 
 
               int position = dicomDirFileName.LastIndexOf("\\"); 
               string directoryPath = dicomDirFileName.Remove(position); 
               string fileName = directoryPath + "\\" + dicomdirDataSet.GetConvertValue(imageElement); 
 
               DicomDataSet imageDataSet = new DicomDataSet(); 
               imageDataSet.Load(fileName, DicomDataSetLoadFlags.None); 
               int imageCount = imageDataSet.GetImageCount(null); 
               if (imageCount != 0) 
                  hasImage = true; 
            } 
            index++; 
            imageElement = dicomdirDataSet.GetNextKey(imageElement, true); 
            imageElement = dicomdirDataSet.GetChildElement(imageElement, true); 
         } 
 
         bool alreadyExists = false; 
         foreach (DataGridViewRow seriesRow in seriesDataGridView.Rows) 
            if (seriesRow.Cells[6].Value.ToString().Trim().Contains((string)row[6])) 
               alreadyExists = true; 
 
         // After retrieving all required series information, add them to the series Grid 
         if (hasImage && !alreadyExists) 
            seriesDataGridView.Rows.Add(row); 
 
         // Get to the next series element. 
         seriesDicomElement = dicomdirDataSet.GetNextKey(seriesDicomElement, true); 
         seriesDicomElement = dicomdirDataSet.GetChildElement(seriesDicomElement, true); 
      } 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.Message, "Error Adding Series", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); 
   } 
} 

View Series for Selected Study

Add a new method named ViewSeriesRowsForThisStudy(int studyRowIndex), which will be called inside the btnAddDICOMDIR_Click event handler as shown above. Add the code below to make it so that only the series associated with the selected study is displayed in the dialog.

C#
private void ViewSeriesRowsForThisStudy(int studyRowIndex) 
{ 
   string studyInstanceUID = studiesDataGridView.Rows[studyRowIndex].Cells[studyInstanceUIDColumn.Name].Value.ToString(); 
 
   bool firstVisible = false; 
   foreach (DataGridViewRow row in seriesDataGridView.Rows) 
   { 
      // Find first series row matching the study instance UID 
      if (row.Cells[7].Value.ToString().Trim().Contains(studyInstanceUID.Trim())) 
      { 
         row.Visible = true; 
         if (!firstVisible) 
         { 
            seriesDataGridView.CurrentCell = row.Cells[0]; 
            firstVisible = true; 
            btnLoad.Enabled = true; 
         } 
      } 
      else 
      { 
         row.Visible = false; 
         if (!firstVisible) 
            btnLoad.Enabled = false; 
      } 
   } 
} 

Select the studiesDataGridView and add an handler(double-click SelectionChanged) for the SelectionChanged event so that when a different study is selected, the displayed series is updated accordingly. Add the code below.

C#
private void studiesDataGridView_SelectionChanged(object sender, EventArgs e) 
{ 
   if (studiesDataGridView.SelectedRows.Count != 0) 
   { 
      string studyInstanceUID = studiesDataGridView.SelectedRows[0].Cells[studyInstanceUIDColumn.Name].Value.ToString(); 
 
      bool firstVisible = false; 
      btnLoad.Enabled = false; 
      foreach (DataGridViewRow seriesRow in seriesDataGridView.Rows) 
      { 
         if (seriesRow.Cells[7].Value.ToString().Trim().Contains(studyInstanceUID.Trim())) 
         { 
            seriesRow.Visible = true; 
            if (!firstVisible) 
            { 
               seriesDataGridView.CurrentCell = seriesRow.Cells[0]; 
               btnLoad.Enabled = true; 
            } 
         } 
         else 
            seriesRow.Visible = false; 
      } 
   } 
} 

Clear Button

Double click on the Clear button to add the click handler and add the code below to it, to remove all loaded studies and series from the dialog.

C#
private void btnClear_Click(object sender, EventArgs e) 
{ 
   studiesDataGridView.Rows.Clear(); 
   seriesDataGridView.Rows.Clear(); 
   btnLoad.Visible = false; 
   btnClear.Visible = false; 
} 

Load Selected Series Code

Double-click on the Load button to add the click handler.

Loading the selected series involves the following:

The MedicalViewerSeriesManager sorts the frames and separates them into sorted Stacks and separate Localizers. Stacks contain frame images sorted based on their locations and help in the creation of 3D objects. Localizers are separate images that have a different angle than the angle of the slices used to create the 3D object.

Add the code below to the btnLoad_Click event handler to load the selected series from DICOM file-set.

C#
private void btnLoad_Click(object sender, EventArgs e) 
{ 
   // Get information from the selected study data-grid row 
   DataGridViewRow studyRow = studiesDataGridView.SelectedRows[0]; 
   string selectedStudyInstanceUID = studyRow.Cells[StudyInstanceUIDColumn.Name].Value.ToString(); 
   string dicomDirFileName = studyRow.Cells[DICOMDIRFileNameColumn.Name].Value.ToString(); 
 
   // Get information from the selected series data grid row 
   DataGridViewRow seriesRow = seriesDataGridView.SelectedRows[0]; 
   string seriesInstanceUID = seriesRow.Cells[SeriesIDColumn.Name].Value.ToString(); 
   int frameCount = Convert.ToInt32(seriesRow.Cells[FrameCountColumn.Name].Value); 
 
   // Load DICOMDIR dataset 
   DicomDataSet dicomdirDataSet = new DicomDataSet(); 
   dicomdirDataSet.Load(dicomDirFileName, DicomDataSetLoadFlags.None); 
 
   // Used to iterate through DICOM elements 
   DicomElement element = null; 
 
   // Get the selected study element 
   DicomElement studyElement = null; 
   DicomElement patientElement = dicomdirDataSet.GetFirstKey(null, true); 
   DicomElement studyInstanceUIDElement = null; 
   while (patientElement != null) 
   { 
      element = dicomdirDataSet.GetChildKey(patientElement); 
      element = dicomdirDataSet.GetChildElement(element, true); 
      while (element != null) 
      { 
         studyInstanceUIDElement = dicomdirDataSet.FindFirstElement(element, DicomTag.StudyInstanceUID, true); 
         if (studyInstanceUIDElement != null) 
         { 
            string studyUID = dicomdirDataSet.GetConvertValue(studyInstanceUIDElement); 
            if (studyUID == selectedStudyInstanceUID) 
            { 
               studyElement = studyInstanceUIDElement; 
               break; 
            } 
         } 
         element = dicomdirDataSet.GetNextKey(element, true); 
         element = dicomdirDataSet.GetChildElement(element, true); 
      } 
      patientElement = dicomdirDataSet.GetNextKey(patientElement, true); 
   } 
 
   // Get the selected series element 
   DicomElement seriesElement = null; 
   DicomElement seriesInformationElement = null; 
   element = dicomdirDataSet.GetChildKey(studyElement); 
   element = dicomdirDataSet.GetChildElement(element, true); 
   while (element != null) 
   { 
      seriesInformationElement = dicomdirDataSet.FindFirstElement(element, DicomTag.SeriesInstanceUID, true); 
      if (seriesInformationElement != null) 
      { 
         string seriesID = dicomdirDataSet.GetConvertValue(seriesInformationElement); 
         if (seriesID == seriesInstanceUID) 
         { 
            seriesElement = seriesInformationElement; 
            break; 
         } 
      } 
      element = dicomdirDataSet.GetNextKey(element, true); 
      element = dicomdirDataSet.GetChildElement(element, true); 
   } 
 
   // Get the path to the location of the series frames in the DICOMDIR 
   int position = dicomDirFileName.LastIndexOf("\\"); 
   string seriesPath = dicomDirFileName.Remove(position); 
 
   // Get path to first image frame in the series 
   string imagePath = ""; 
   DicomElement imageIDElement = null; 
   element = dicomdirDataSet.GetChildKey(seriesElement); 
   element = dicomdirDataSet.GetChildElement(element, true); 
   while (element != null) 
   { 
      imageIDElement = dicomdirDataSet.FindFirstElement(element, DicomTag.ReferencedFileID, true); 
      if (imageIDElement != null) 
      { 
         imagePath = seriesPath + "\\" + dicomdirDataSet.GetConvertValue(imageIDElement); 
         break; 
      } 
   } 
 
   // Load the information from each frame image and store in imageData 
   DicomDataSet imageDataSet; 
   double[] doubleArray = new double[3]; 
   string _referenceUID = ""; 
   MedicalViewerImageData imageData = new MedicalViewerImageData(); 
   List<MedicalViewerImageData> imageDataList = new List<MedicalViewerImageData>(); 
   string[] userDataTags = new string[6]; 
 
   for (int imageIndex = 0; imageIndex < frameCount; imageIndex++) 
   { 
      // Load image dataset 
      imageDataSet = new DicomDataSet(); 
      imageDataSet.Load(imagePath, DicomDataSetLoadFlags.None); 
 
      // Get User Data Tags 
      if (imageIndex == 0) 
      { 
         userDataTags[0] = GetDicomTag(imageDataSet, DicomTag.InstitutionName); 
         userDataTags[1] = GetDicomTag(imageDataSet, DicomTag.PatientName); 
         userDataTags[2] = GetDicomTag(imageDataSet, DicomTag.PatientAge); 
         userDataTags[3] = GetDicomTag(imageDataSet, DicomTag.PatientBirthDate); 
         userDataTags[4] = GetDicomTag(imageDataSet, DicomTag.PatientSex); 
         userDataTags[5] = GetDicomTag(imageDataSet, DicomTag.PatientID); 
      } 
 
      // Create new imageData instance for each frame 
      imageData = new MedicalViewerImageData(); 
 
      // Image 
      DicomGetImageFlags dicomGetImageFlags = DicomGetImageFlags.AutoDetectInvalidRleCompression | DicomGetImageFlags.AutoApplyModalityLut | DicomGetImageFlags.AutoApplyVoiLut | DicomGetImageFlags.AutoScaleModalityLut | DicomGetImageFlags.AutoScaleVoiLut; 
      imageData.Image = imageDataSet.GetImage(null, 0, 0, RasterByteOrder.Gray, dicomGetImageFlags); 
 
      // Frame of Reference UID 
      patientElement = imageDataSet.FindFirstElement(null, DicomTag.FrameOfReferenceUID, true); 
      if (patientElement != null) 
         imageData.FrameOfReferenceUID = imageDataSet.GetConvertValue(patientElement); 
      else 
         imageData.FrameOfReferenceUID = ""; 
 
      // Skip if the reference ID is not the same for first frame 
      if (imageIndex == 0) 
         _referenceUID = imageData.FrameOfReferenceUID; 
      else if (_referenceUID != imageData.FrameOfReferenceUID) 
         continue; 
 
      // Echo Number 
      int echoNumber = -1; 
      DicomElement echoElement = imageDataSet.FindFirstElement(null, DicomTag.EchoNumber, true); 
      if (echoElement != null) 
      { 
         string patientID = String.Empty; 
         patientID = imageDataSet.GetConvertValue(echoElement); 
         echoNumber = Convert.ToInt32(patientID); 
      } 
      imageData.EchoNumber = echoNumber; 
 
      // Sequence Name 
      patientElement = imageDataSet.FindFirstElement(null, DicomTag.SequenceName, true); 
      if (patientElement != null) 
      { 
         imageData.SequenceName = imageDataSet.GetConvertValue(patientElement); 
      } 
 
      // Instance Number 
      patientElement = imageDataSet.FindFirstElement(null, DicomTag.InstanceNumber, true); 
      if (patientElement != null) 
      { 
         int[] integerArray; 
         integerArray = imageDataSet.GetIntValue(patientElement, 0, 1); 
         if (integerArray.Length != 0) 
            imageData.InstanceNumber = integerArray[0]; 
      } 
 
      // Image Position 
      patientElement = imageDataSet.FindFirstElement(null, DicomTag.ImagePositionPatient, true); 
      doubleArray = imageDataSet.GetDoubleValue(patientElement, 0, 3); 
      if (doubleArray.Length == 0) 
         doubleArray = new double[3]; 
      imageData.ImagePosition = Point3D.FromDoubleArray(doubleArray); 
 
      // Image Orientation 
      patientElement = imageDataSet.FindFirstElement(null, DicomTag.ImageOrientationPatient, true); 
      if (patientElement != null) 
         imageData.ImageOrientation = imageDataSet.GetConvertValue(patientElement); 
 
      // Pixel Spacing 
      patientElement = imageDataSet.FindFirstElement(null, DicomTag.PixelSpacing, true); 
      doubleArray = imageDataSet.GetDoubleValue(patientElement, 0, 2); 
      if (doubleArray.Length != 0) 
         imageData.PixelSpacing = new Point2D((float)doubleArray[0], (float)doubleArray[1]); 
      else 
         imageData.PixelSpacing = new Point2D(1, 1); 
 
      // Add the to the list of image data to be loaded 
      imageDataList.Add(imageData); 
 
      // Get path to the next frame 
      DicomElement nextImageElement = null; 
      element = dicomdirDataSet.GetNextKey(element, true); 
      element = dicomdirDataSet.GetChildElement(element, true); 
      while (element != null) 
      { 
         nextImageElement = dicomdirDataSet.FindFirstElement(element, DicomTag.ReferencedFileID, true); 
         if (element != null) 
         { 
            imagePath = seriesPath + "\\" + dicomdirDataSet.GetConvertValue(nextImageElement); 
            break; 
         } 
      } 
      if (element == null) 
         imagePath = ""; 
   } 
 
   // Sort the images based on their position 
   MedicalViewerSeriesManager seriesManager = new MedicalViewerSeriesManager(); 
   seriesManager.Sort(imageDataList); 
 
   // 3D stacks 
 
   for (int stackIndex = 0; stackIndex < seriesManager.Stacks.Count; stackIndex++) 
   { 
      int count = seriesManager.Stacks[stackIndex].Items.Count; 
      for (int frameIndex = 0; frameIndex < count; frameIndex++) 
      { 
         imageData = seriesManager.Stacks[0].Items[frameIndex]; 
 
         SendFrameLoaded( 
            imageData.Image, 
            frameIndex, 
            count, 
            imageData.PixelSpacing, 
            imageData.ImagePosition, 
            imageData.ImageOrientationArray, 
            userDataTags); 
      } 
   } 
 
   // Localizers 
 
   foreach (MedicalViewerSeriesLocalizer localizer in seriesManager.Localizers) 
   { 
      imageData = localizer.LocalizerData; 
 
      SendFrameLoaded( 
            imageData.Image, 
            0, 
            1, 
            imageData.PixelSpacing, 
            imageData.ImagePosition, 
            imageData.ImageOrientationArray, 
            userDataTags); 
   } 
 
   this.DialogResult = DialogResult.OK; 
} 
 
string GetDicomTag(DicomDataSet ds, long tag) 
{ 
   DicomElement patientElement = ds.FindFirstElement(null, tag, true); 
   if (patientElement != null) 
      return ds.GetConvertValue(patientElement); 
   return null; 
} 

SendFramesLoaded Event Handler

Add a FrameLoadedEventArgs class, inside the SeriesBrowser class, to use for sending the extracted sorted information for each frame to the main form.

C#
public class FrameLoadedEventArgs : EventArgs 
{ 
   private RasterImage _image; 
   private int _frameIndex; 
   private int _pageCount; 
   private Point2D _pixelSpacing; 
   private Point3D _imagePosition; 
   private float[] _imageOrientation; 
   private string[] _userDataTags; 
 
   public FrameLoadedEventArgs(RasterImage image, int frameIndex, int pageCount, Point2D pixelSpacing, Point3D imagePosition, float[] imageOrientation, string[] userDataTags) 
   { 
      _image = image; 
      _frameIndex = frameIndex; 
      _pageCount = pageCount; 
      _pixelSpacing = pixelSpacing; 
      _imagePosition = imagePosition; 
      _imageOrientation = imageOrientation; 
      _userDataTags = userDataTags; 
   } 
 
   public RasterImage Image { get { return _image; } } 
   public int FrameIndex { get { return _frameIndex; } }  
   public int PageCount { get { return _pageCount; } } 
   public Point2D PixelSpacing { get { return _pixelSpacing; } } 
   public Point3D ImagePosition { get { return _imagePosition; } } 
   public float[] ImageOrientation { get { return _imageOrientation; } } 
   public string[] UserDataTags { get { return _userDataTags; } } 
} 
 
public delegate void FrameLoadedEventHandler(object sender, FrameLoadedEventArgs e); 
public event FrameLoadedEventHandler FrameLoaded; 
 
void SendFrameLoaded(RasterImage image, int frameIndex, int pageCount, Point2D pixelSpacing, Point3D imagePosition, float[] imageOrientation, string[] userDataTags) 
{ 
   // If the event handler is not registered, then exit 
   if (FrameLoaded == null) 
      return; 
 
   FrameLoadedEventArgs args = new FrameLoadedEventArgs(image, frameIndex, pageCount, pixelSpacing, imagePosition, imageOrientation, userDataTags); 
   FrameLoaded(this, args); 
} 

Load the Passed Frames in the Viewer

View the code for Form1 and add the following to the using block at the top.

C#
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Windows.Forms; 
using Leadtools; 
using Leadtools.Dicom; 
using Leadtools.Medical3D; 
using Leadtools.MedicalViewer; 
using static <Namespace>.SeriesBrowser; // Replace with Namespace 

Add the following to the global variables:

C#
private MedicalViewer _medicalViewer; 
private MedicalViewerMultiCell _cell; 
private Medical3DControl _control3D; 
private List<Medical3DControl> _controls3D = new List<Medical3DControl>(); 
private SeriesBrowser seriesBrowserDialog; 

In the Solution Explorer, open the designer for Form1 and add a new menu item to the File menu strip with the text Load &DICOMDIR. Double-click on this menu item and use the following code for the handler.

C#
private void loadDICOMDIRToolStripMenuItem_Click(object sender, EventArgs e) 
{ 
   // Clear existing cells and 3D controls 
   _control3D = null; 
   if (_medicalViewer.Cells.Count > 0) 
      _medicalViewer.Cells.Clear(); 
 
   // Clear check-marks from cell type menu 
   foreach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems) 
      toolStripMenuItem.Checked = false; 
 
   // Reset viewer Columns and Rows 
   _medicalViewer.Columns = 1; 
   _medicalViewer.Rows = 1; 
 
   if (seriesBrowserDialog == null) 
   { 
      seriesBrowserDialog = new SeriesBrowser(); 
      seriesBrowserDialog.FrameLoaded += new FrameLoadedEventHandler(seriesBrowserDialog_FrameLoaded); 
   } 
   seriesBrowserDialog.ShowDialog(); 
} 

Add the following code to the FrameLoadedEventHandler handle, created above, which will fire for each passed frame from the dialog and create the multi-cell and 3D object used for each passed frame.

C#
private RasterImage _image; 
 
void seriesBrowserDialog_FrameLoaded(object sender, FrameLoadedEventArgs e) 
{ 
   try 
   { 
      if (e.FrameIndex == 0) 
      { 
         _image = e.Image.Clone(); 
         Initialize3DControl(e.Image.Width, e.Image.Height, e.PageCount); 
      } 
      else 
         _image.AddPage(e.Image.Clone()); 
 
      // Add image pages to the 3D control frame 
             
      if (_control3D != null) 
         _control3D.ObjectsContainer.Objects[0].MemoryEfficientSetFrame(e.Image.Clone(), e.FrameIndex, e.ImagePosition, true); 
 
      if (e.FrameIndex + 1 == e.PageCount) // Finished loading last frame 
      { 
         InitializeMultiCell(_image); 
 
         if (_cell != null) 
         { 
            // Set Tags 
            SetTags(e.UserDataTags); 
 
            // Add column if localizer cell added previously 
            if (_medicalViewer.Cells.Count > 0) 
               _medicalViewer.Columns = _medicalViewer.Columns + 1; 
 
            // Clear existing cells and add initialized cell to the viewer 
            _medicalViewer.Cells.Add(_cell); 
 
            if (_control3D != null) 
            { 
               // Set tags 
               Set3DTags(); 
 
               // Finish 
               _control3D.ObjectsContainer.Objects[0].MemoryEfficientEnd(e.ImageOrientation, e.PixelSpacing); 
            } 
         } 
      } 
   } 
   catch (Exception ex) 
   { 
      MessageBox.Show(ex.ToString()); 
   } 
} 

Modify Current Code to Load Multiple Cells and 3D Objects

Because DICOM file-sets may contain multiple stacks or localizers, modify the code to the below code to support loading multiple cells and 3D controls.

Add two List collections, _cells and _controls3D, to the global variables.

C#
private MedicalViewer _medicalViewer; 
private MedicalViewerMultiCell _cell; 
private Medical3DControl _control3D; 
private SeriesBrowser seriesBrowserDialog; 
private List<MedicalViewerMultiCell> _cells = new List<MedicalViewerMultiCell>(); 
private List<Medical3DControl> _controls3D = new List<Medical3DControl>(); 

In the loadDICOMToolStripMenuItem_Click and seriesBrowserDialog_FrameLoaded handlers, modify the code to add the created cell to the _cells collection. In addition, add the created 3D control to the _controls3D collection after it is finished being set.

C#
// Add initialized cell 
_medicalViewer.Cells.Add(_cell); 
_cells.Add(_cell); 
 
// Finish 
_control3D.ObjectsContainer.Objects[0].MemoryEfficientEnd(_cell.ImageOrientation, _cell.PixelSpacing); 
_controls3D.Add(_control3D); 

Use the _cells and _controls3D collections in the cellTypeToolStripMenuItem_DropDownOpening, _menuVolumeVRT_Click, and _menu2DCell_Click event handlers when selecting the cell type from the menu strip to display multiple loaded cells or 3D controls.

C#
private void cellTypeToolStripMenuItem_DropDownOpening(object sender, EventArgs e) 
{ 
   bool Allow3D = false; 
   bool Allow2D = false; 
 
   if (_cells.Count > 0) 
      Allow2D = true; 
   if (_controls3D.Count > 0) 
      Allow3D = true; 
 
   _menuVolumeVRT.Enabled = Allow3D; 
   _menu2DCell.Enabled = Allow2D; 
} 
 
private void _menuVolumeVRT_Click(object sender, EventArgs e) 
{ 
   foreach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems) 
      toolStripMenuItem.Checked = false; 
   _menuVolumeVRT.Checked = true; 
 
   if (_medicalViewer.Cells[0] != _controls3D[0]) 
   { 
      _medicalViewer.Cells.Clear(); 
      foreach (Medical3DControl control3D in _controls3D) 
      { 
         if(_controls3D.Count > _medicalViewer.Rows * _medicalViewer.Columns) 
            if(_medicalViewer.Rows == _medicalViewer.Columns) 
               _medicalViewer.Rows++; 
            else 
               _medicalViewer.Columns++; 
 
         _medicalViewer.Cells.Add(control3D); 
      } 
   } 
} 
 
private void _menu2DCell_Click(object sender, EventArgs e) 
{ 
   foreach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems) 
      toolStripMenuItem.Checked = false; 
   _menu2DCell.Checked = true; 
 
   if (_medicalViewer.Cells[0] != _cells[0]) 
   { 
      _medicalViewer.Cells.Clear(); 
      foreach (MedicalViewerMultiCell cell in _cells) 
      { 
         if (_cells.Count > _medicalViewer.Rows * _medicalViewer.Columns) 
            if (_medicalViewer.Rows == _medicalViewer.Columns) 
               _medicalViewer.Rows++; 
            else 
               _medicalViewer.Columns++; 
 
         _medicalViewer.Cells.Add(cell); 
      } 
   } 
} 

In the loadDICOMDIRToolStripMenuItem_Click and seriesBrowserDialog_FrameLoaded handlers, clear the collections before continuing with loading the image.

Finally, reset the MedicalViewer Rows and Columns to 1 when loading a new image.

C#
// loadDICOMDIRToolStripMenuItem_Click and seriesBrowserDialog_FrameLoaded 
{  
   // Clear existing cells and 3D controls 
   if (_controls3D.Count > 0) 
   { 
      _controls3D.Clear(); 
      _control3D = null; 
   } 
   if (_medicalViewer.Cells.Count > 0) 
      _medicalViewer.Cells.Clear(); 
 
   // Clear check-marks from cell type menu 
   foreach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems) 
      toolStripMenuItem.Checked = false; 
 
   // Reset viewer Columns and Rows 
   _medicalViewer.Columns = 1; 
   _medicalViewer.Rows = 1; 

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 displays a series browser which loads DICOM medical studies and series from DICOM file-sets containing a DICOMDIR file.

Sample DICOM file-sets containing 3D studies and series can be found here: https://download.leadtools.com/images/3d/

Browse DICOMDIR
Load in Viewer

Wrap-up

This tutorial showed how to add a series browser dialog that displays the studies and series in a DICOM file-set using the DICOMDIR file. In addition, the dialog loads and passes the frame images that were sorted by the MedicalViewerSeriesManager class to the medical viewer to be displayed.

See Also

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


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