Viewing and Editing Meta Data in C#

We have an example project for C# that shows how to use the csXImage ActiveX control to view and edit meta data in JPG and TIFF images. 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.

The Combo Box that contains the various options is set when the form loads, so it is easier to see the values by viewing the code. What is less easy to see is that there are a number of Group Box controls that are on top of each other and each of these is used to display the different sections of the meta data values. The Group Box that is displayed at the front is selected by the Combo Box.

When the application runs the user can open a JPEG or TIFF file for viewing. The file is selected using the OpenFileDialog control and the LoadFromFile csXImage method. Below is a screenshot after loading an image and selecting the EXIF Data frame.

View and edit JPEG metadata in C#

When the image is loaded the following code is run to fill the text fields in the frames. Most of the IPTC fields are exposed as string properties so it is a simple matter of reading them and copying them to a caption on the form. In csXImage the IPTC properties are prefixed with FFO_ (for File Info). Caption, CaptionWriter, Headline and SpecialInstructions are examples of these properties. The keywords and supplementary categories are an exception because these allow multiple values. It requires a loop using the count property to read these values into a list box. Urgency is another exception because it contains a number. The Date Created is converted into a short date using the C# DateTime.ToShortDateString method, after first checking it is not zero.

The Exif data is stored in csXImage as an array of strings, with some helper functions to allow reading and writing. The ExifCount property is used to define a loop and each Exif property and its name is read and added to a list box.

private void readFileInfo()
{
  if (axImageBox1.ImageLoaded)
  {
    //Caption
    txtCaption.Text = axImageBox1.FFO_Caption;
    txtCaptionWriter.Text = axImageBox1.FFO_CaptionWriter;
    txtHeadline.Text = axImageBox1.FFO_Headline;
    txtSpecInst.Text = axImageBox1.FFO_SpecialInstructions;

    //Keywords
    lstKeywords.Items.Clear();
    for (int i = 0; i < axImageBox1.FFO_KeywordsCount; i++)
      lstKeywords.Items.Add(axImageBox1.get_FFO_Keywords(i));

    //Categories
    txtCategory.Text = axImageBox1.FFO_Category;
    lstSupCats.Items.Clear();
    for (int i = 0; i < axImageBox1.FFO_SuppCatCount; i++)
      lstSupCats.Items.Add(axImageBox1.get_FFO_SuppCat(i));
    if (axImageBox1.FFO_Urgency != 0)
      cboUrgency.Text = axImageBox1.FFO_Urgency.ToString();
    else
      cboUrgency.Text = "None";

    //Credits
    txtByline.Text = axImageBox1.FFO_Byline;
    txtBylineTitle.Text = axImageBox1.FFO_BylineTitle;
    txtCredit.Text = axImageBox1.FFO_Credit;
    txtSource.Text = axImageBox1.FFO_Source;

    //Origin
    txtObjectName.Text = axImageBox1.FFO_ObjectName;
    if (axImageBox1.FFO_DateCreated.ToOADate() != 0)
      txtDate.Text = axImageBox1.FFO_DateCreated.ToShortDateString();
    txtCity.Text = axImageBox1.FFO_City;
    txtProvince.Text = axImageBox1.FFO_ProvinceState;
    txtCountry.Text = axImageBox1.FFO_CountryName;
    txtOTR.Text = axImageBox1.FFO_OTR;

    //Copyright & URL
    if (axImageBox1.FFO_CopyrightFlag)
      chkIsCopyright.Checked = true;
    else
      chkIsCopyright.Checked = false;
    txtCopyright.Text = axImageBox1.FFO_CopyrightNotice;
    txtImageURL.Text = axImageBox1.FFO_ImageURL;

    //Creator Contact
    txtCreator.Text = axImageBox1.FFO_Author;
    txtAddress.Text = axImageBox1.FFO_CiAdrExtAdr;
    txtCity2.Text = axImageBox1.FFO_CiAdrCity;
    txtRegion.Text = axImageBox1.FFO_CiAdrRegion;
    txtPostCode.Text = axImageBox1.FFO_CiAdrPcode;
    txtCountry2.Text = axImageBox1.FFO_CiAdrCtry;

    //Exif Data
    lstExif.Items.Clear();
    for (int i = 0; i < axImageBox1.ExifCount; i++)
      lstExif.Items.Add(axImageBox1.get_ExifName(i) + ": " +
        axImageBox1.get_ExifValueByIndex(i));
    }
  }

Writing the IPTC data after editing is just the reverse process to reading and it is carried out by the WriteFileInfo function in the demo project. Here is an extract from that function. The keywords are added singly using FFO_KeywordsAdd.

  //Caption
  axImageBox1.FFO_Caption = txtCaption.Text;
  axImageBox1.FFO_CaptionWriter = txtCaptionWriter.Text;
  axImageBox1.FFO_Headline = txtHeadline.Text;
  axImageBox1.FFO_SpecialInstructions = txtSpecInst.Text;

  //Keywords
  axImageBox1.FFO_KeywordsClear();
  for (int i = 0; i < lstKeywords.Items.Count; i++)
    axImageBox1.FFO_KeywordsAdd(lstKeywords.Items[i].ToString());

Exif attributes are edited using another form, shown below.

C# ActiveX example to make an EXIF and IPTC editor

The following code is used by the form to collect the Exif tag name and its new value and then the form closes itself. exifAttName and exifAttVal are global variables stored in a separate static class, allowing them to be used across the project.

private void cmdAddOk_Click(object sender, EventArgs e)
{
  globals.exifAttName = txtAddName.Text;
  globals.exifAttValue = txtAddVal.Text;
  fAdd.ActiveForm.Close();
}

Here is the code that calls the modal form and processes the data returned. ExifSetAttribute requires the exact name of the Exif attribute and the new value as a string, even if the attribute is a numerical or date value. It returns a value of true if the attribute is set successfully. Exif values are usually set by the device which creates the image and it is unlikely that there will be a need to set them later, except perhaps the ImageDescription or one of the date/time values.

private void cmdExifAdd_Click(object sender, EventArgs e)
{
  fAdd addForm = new fAdd();
  addForm.ShowDialog();
  addForm.Dispose();

  if (!axImageBox1.ExifSetAttribute(globals.exifAttName, globals.exifAttValue))
    MessageBox.Show("Invalid Exif Attribute Specification");

  exifUpdate();
}