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 |
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.
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:
Leadtools.Medical.Viewer.WinFormsLeadtools.Jpeg2000If using local DLL references, the following DLLs are needed.
The DLLs are located at <INSTALL_DIR>\LEADTOOLS22\Bin\Dotnet4\x64:
Leadtools.dllLeadtools.Codecs.Cmp.dllLeadtools.Codecs.J2k.dllLeadtools.Dicom.dllLeadtools.MedicalViewer.dllLeadtools.Medical3D.dllFor a complete list of which DLL files are required for your application, refer to Files to be Included With Your Application.
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:
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:
Name: btnClear, Enabled: False, Text: Clear, Visible: False

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:
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.
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 studyint lastRowIndex = studiesDataGridView.Rows.Count - 1;studiesDataGridView.CurrentCell = studiesDataGridView.Rows[lastRowIndex].Cells[0];ViewSeriesRowsForThisStudy(lastRowIndex);// Show and enable ButtonsbtnLoad.Visible = true;btnClear.Visible = true;dicomdirDataSet.Dispose();}}catch (Exception ex){MessageBox.Show(ex.Message, dialog.FileName, MessageBoxButtons.OK);}}
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.
private void AddStudyRows(DicomDataSet dicomdirDataSet, string dicomdirFileName){// Get Patient Dicom ElementDicomElement 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 NamepatientNameInformation = dicomdirDataSet.FindFirstElement(null, DicomTag.PatientName, false);if (patientNameInformation != null)patientName = dicomdirDataSet.GetConvertValue(patientNameInformation);// Patient IDpatientIdInformation = dicomdirDataSet.FindFirstElement(null, DicomTag.PatientID, false);if (patientIdInformation != null)patientId = dicomdirDataSet.GetConvertValue(patientIdInformation);while (patientNameInformation != null){// Get the first StudystudyElement = dicomdirDataSet.GetChildKey(patientElement);studyElement = dicomdirDataSet.GetChildElement(studyElement, true);while (studyElement != null){row[0] = patientName;row[1] = patientId;// Rest of Tagsfor (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 Gridif (!alreadyExists)studiesDataGridView.Rows.Add(row);// Add series rows for all studies in the DICOMDIRAddSeriesRows(dicomdirDataSet, studyElement, row[6], dicomdirFileName);// Go to the next study elementstudyElement = dicomdirDataSet.GetNextKey(studyElement, true);studyElement = dicomdirDataSet.GetChildElement(studyElement, true);}// Go to the next patient element// Next Patient NamepatientNameInformation = dicomdirDataSet.FindNextElement(patientNameInformation, false);if (patientNameInformation != null)patientName = dicomdirDataSet.GetConvertValue(patientNameInformation);// Next Patient IDpatientIdInformation = 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 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.
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 CountDicomElement 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 imagesDicomElement 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 Gridif (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);}}
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.
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 UIDif (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.
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;}}elseseriesRow.Visible = false;}}}
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.
private void btnClear_Click(object sender, EventArgs e){studiesDataGridView.Rows.Clear();seriesDataGridView.Rows.Clear();btnLoad.Visible = false;btnClear.Visible = false;}
Double-click on the Load button to add the click handler.
Loading the selected series involves the following:
MedicalViewerSeriesManager class to sort the frames into the correct order using image position.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.
private void btnLoad_Click(object sender, EventArgs e){// Get information from the selected study data-grid rowDataGridViewRow 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 rowDataGridViewRow seriesRow = seriesDataGridView.SelectedRows[0];string seriesInstanceUID = seriesRow.Cells[SeriesIDColumn.Name].Value.ToString();int frameCount = Convert.ToInt32(seriesRow.Cells[FrameCountColumn.Name].Value);// Load DICOMDIR datasetDicomDataSet dicomdirDataSet = new DicomDataSet();dicomdirDataSet.Load(dicomDirFileName, DicomDataSetLoadFlags.None);// Used to iterate through DICOM elementsDicomElement element = null;// Get the selected study elementDicomElement 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 elementDicomElement 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 DICOMDIRint position = dicomDirFileName.LastIndexOf("\\");string seriesPath = dicomDirFileName.Remove(position);// Get path to first image frame in the seriesstring 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 imageDataDicomDataSet 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 datasetimageDataSet = new DicomDataSet();imageDataSet.Load(imagePath, DicomDataSetLoadFlags.None);// Get User Data Tagsif (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 frameimageData = new MedicalViewerImageData();// ImageDicomGetImageFlags dicomGetImageFlags = DicomGetImageFlags.AutoDetectInvalidRleCompression | DicomGetImageFlags.AutoApplyModalityLut | DicomGetImageFlags.AutoApplyVoiLut | DicomGetImageFlags.AutoScaleModalityLut | DicomGetImageFlags.AutoScaleVoiLut;imageData.Image = imageDataSet.GetImage(null, 0, 0, RasterByteOrder.Gray, dicomGetImageFlags);// Frame of Reference UIDpatientElement = imageDataSet.FindFirstElement(null, DicomTag.FrameOfReferenceUID, true);if (patientElement != null)imageData.FrameOfReferenceUID = imageDataSet.GetConvertValue(patientElement);elseimageData.FrameOfReferenceUID = "";// Skip if the reference ID is not the same for first frameif (imageIndex == 0)_referenceUID = imageData.FrameOfReferenceUID;else if (_referenceUID != imageData.FrameOfReferenceUID)continue;// Echo Numberint 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 NamepatientElement = imageDataSet.FindFirstElement(null, DicomTag.SequenceName, true);if (patientElement != null){imageData.SequenceName = imageDataSet.GetConvertValue(patientElement);}// Instance NumberpatientElement = 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 PositionpatientElement = 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 OrientationpatientElement = imageDataSet.FindFirstElement(null, DicomTag.ImageOrientationPatient, true);if (patientElement != null)imageData.ImageOrientation = imageDataSet.GetConvertValue(patientElement);// Pixel SpacingpatientElement = 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]);elseimageData.PixelSpacing = new Point2D(1, 1);// Add the to the list of image data to be loadedimageDataList.Add(imageData);// Get path to the next frameDicomElement 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 positionMedicalViewerSeriesManager seriesManager = new MedicalViewerSeriesManager();seriesManager.Sort(imageDataList);// 3D stacksfor (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);}}// Localizersforeach (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;}
Add a FrameLoadedEventArgs class, inside the SeriesBrowser class, to use for sending the extracted sorted information for each frame to the main form.
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 exitif (FrameLoaded == null)return;FrameLoadedEventArgs args = new FrameLoadedEventArgs(image, frameIndex, pageCount, pixelSpacing, imagePosition, imageOrientation, userDataTags);FrameLoaded(this, args);}
View the code for Form1 and add the following to the using block at the top.
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:
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.
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 menuforeach (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.
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 frameif (_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 TagsSetTags(e.UserDataTags);// Add column if localizer cell added previouslyif (_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 tagsSet3DTags();// Finish_control3D.ObjectsContainer.Objects[0].MemoryEfficientEnd(e.ImageOrientation, e.PixelSpacing);}}}}catch (Exception ex){MessageBox.Show(ex.ToString());}}
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.
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.
// 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.
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.
// loadDICOMDIRToolStripMenuItem_Click and seriesBrowserDialog_FrameLoaded{// Clear existing cells and 3D controlsif (_controls3D.Count > 0){_controls3D.Clear();_control3D = null;}if (_medicalViewer.Cells.Count > 0)_medicalViewer.Cells.Clear();// Clear check-marks from cell type menuforeach (ToolStripMenuItem toolStripMenuItem in cellTypeToolStripMenuItem.DropDownItems)toolStripMenuItem.Checked = false;// Reset viewer Columns and Rows_medicalViewer.Columns = 1;_medicalViewer.Rows = 1;
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/


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.