View and edit multipage TIFFs with C#

We have an example project for C# that shows how to display and edit multipage TIFF files. Download the project code.

The demo uses the trial version of csXImage, which must must be registered on the development computer and the licence file must be in the same folder as the .ocx control. Running our installer will have performed the registration and unpacking of files.

When the C# form is loaded, and event runs to populate the combo boxes to request the number of pages to scan. This makes it easier to follow than setting the values in the Visual Studio IDE.

Here is a screenshot of the application after the form has loaded. It is simply a csXImage control in a form with some buttons to activate the different functions. These are enables and disabled as appropriate.

C# project to view and edit multipage TIFF images

The csXImage control can only hold one image for display at any time so the method used to view multiple images is to store them in a List of variables of type Object. This List, as well as the total number of images and the current image, is declared globally.

int imageCount;
int currentImage;
List<Object> pages = new List<Object>();

The code used to load the image contains several features worth noting. First, the image is selected by the user using the LoadDialog method. As only one page can be read by csXImage at any time, the first page is read by LoadDialog by setting readImageNumber to one. Then this first image is stored in the Pages List by exporting it from csXImage using WriteBinary and it is exported in bitmap format. Any format could have been used but bitmap has been chosen because it requires less processing than the compressed types, although it does require more memory.

The file selected by LoadDialog can be found by reading the LastFileName property. This is used to find the total number of pages in the image, using ImageCount. Now a loop can be formed to read each page in turn and write it to the Pages List variable. After completion of the loop, the first image is loaded into csXImage from the List, using the ReadBinary2 method.

Finally, some buttons are enabled if there is at least 1 page and some more are enabled if there are multiple pages.

Note: Older versions of csXImage use the command ReadBinary which took an extra parameter to define the file format, although this parameter had become redundant as images were read regardles of the format. The command ReadBinary2 has been introduced for simplification.

private void cmdLoad_Click(object sender, EventArgs e)
{
  axImageBox1.ReadImageNumber = 1;
  if (axImageBox1.LoadDialog())
  {
    pages.Clear();
    pages.Add(axImageBox1.WriteBinary(csXImageTrial.TxGraphicsFormat.gfBMP));
    imageCount = axImageBox1.ImageCount(axImageBox1.LastFileName);
    if (imageCount > 1)
    {
      for (int i=2; i <= imageCount; i++)
      {
        axImageBox1.ReadImageNumber = i;
        axImageBox1.LoadFromFile(axImageBox1.LastFileName);
        pages.Add(axImageBox1.WriteBinary(csXImageTrial.TxGraphicsFormat.gfBMP));
        lblPageNumText.Text = "Loading - Page " + i.ToString() + " of "
          + imageCount.ToString();
        fMain.ActiveForm.Refresh();
      }
    }
    currentImage = 1;
    axImageBox1.ReadBinary2(pages[currentImage - 1]);
    lblPageNumText.Text = "Page " + currentImage.ToString() + " of "
      + imageCount.ToString();

    if (imageCount > 0)
    {
      cmdSave.Enabled = true;
      cmdRotate90.Enabled = true;
      cmdRotate180.Enabled = true;
      cmdRotate270.Enabled = true;
      cmdCrop.Enabled = true;
      cmdDespeckle.Enabled = true;

      if (imageCount > 1)
      {
        cmdPrev.Enabled = true;
        cmdNext.Enabled = true;
      }
      else
      {
        cmdPrev.Enabled = false;
        cmdNext.Enabled = false;
      }
    }
  }
}

Stepping forwards and backwards through the image is relatively simple. The new page is read from the Pages List using ReadBinary2 and the currentImage variable is updated.

private void cmdNext_Click(object sender, EventArgs e)
{
  if (currentImage < imageCount)
  {
    currentImage++;
    axImageBox1.ReadBinary2(pages[currentImage - 1]);
    lblPageNumText.Text = "Page " + currentImage.ToString() + " of "
      + imageCount.ToString();
  }
}

private void cmdPrev_Click(object sender, EventArgs e)
{
  if (currentImage > 1)
  {
    currentImage--;
    axImageBox1.ReadBinary2(pages[currentImage - 1]);
    lblPageNumText.Text = "Page " + currentImage.ToString() + " of "
      + imageCount.ToString();
  }
}

We will leave the description of the scanning code because that is covered in the Twain Demo and the ADF Demo. The only significant difference is that in this example each scanned page is stored in the List variable.

To save the image, each page must be added to a multipage TIFF in the memory of csXImage using the AddToTIF method. Each image is loaded into csXImage in turn and AddToTIF is called. Any settings that apply to each page, such as Compression and ColorFormat must be set before calling AddToTIF. The WriteTIFDialog method is used so that the user can specify the location and name of the saved file. Finally the current image is loaded back into the control.

private void cmdSave_Click(object sender, EventArgs e)
{
  for (int i = 1; i <= imageCount; i++)
  {
    axImageBox1.ReadBinary2(pages[i - 1]);
    lblPageNumText.Text = "Saving - Page " + currentImage.ToString() + " of "
      + imageCount.ToString();
    if (axImageBox1.ColorFormat == csXImageTrial.TxColorFormat.cfMonoBW)
      axImageBox1.Compression = csXImageTrial.TxCompression.cmGroup4;
    else
      axImageBox1.Compression = csXImageTrial.TxCompression.cmNone;
    axImageBox1.AddToTIF(0);
  }
  axImageBox1.WriteTIFDialog();
  axImageBox1.ReadBinary2(pages[currentImage - 1]);
  lblPageNumText.Text = "Page " + currentImage.ToString() + " of "
    + imageCount.ToString();
}