Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a CheckBox in Word using OpenXML

How does one get a handle to a CheckBox control that's embedded in a Word document using OpenXML?

You would think that either Paragraph.ControlPropertiesPart or Paragraph.Descendents() would achieve something but in every single case I get a null type returned.

I can traverse down the document tree using the actual XML structure, but this seems cumbersome.

Suggestions welcome.

like image 548
Phil.Wheeler Avatar asked Feb 03 '23 11:02

Phil.Wheeler


1 Answers

The code below shows how to enumerate all checkboxes in a word document by using the Decendants<CheckBox>() method on the docuement's body.

using (WordprocessingDocument doc = WordprocessingDocument.Open("c:\\temp\\checkbox.docx", true))
{
  foreach (CheckBox cb in doc.MainDocumentPart.Document.Body.Descendants<CheckBox>())
  {
    Console.Out.WriteLine(cb.LocalName);

    FormFieldName cbName = cb.Parent.ChildElements.First<FormFieldName>();
    Console.Out.WriteLine(cbName.Val);

    DefaultCheckBoxFormFieldState defaultState = cb.GetFirstChild<DefaultCheckBoxFormFieldState>();
    Checked state = cb.GetFirstChild<Checked>();

    Console.Out.WriteLine(defaultState.Val.ToString());

    if (state.Val == null) // In case checkbox is checked the val attribute is null
    {
      Console.Out.WriteLine("CHECKED");
    }
    else
    {
      Console.Out.WriteLine(state.Val.ToString());
    }
  }
}

To determine the name of a given checkbox input element you have to access the Parent property of the CheckBox instance and then search for the FormFieldName element (to assign a name to a checkbox use the Properties Dialog in Microsoft Word).

The DefaultCheckBoxFormFieldState Val property holds the default state for the checkbox. Furthermore the Val property of the Checked element holds the actual checked state of the CheckBox instance. Note, for Microsoft Word 2007 the Val property is null if the checkbox is checked.

BEGIN EDIT

I'd like to extend my answer. In fact, there are two kinds of checkbox controls on the MS Word developer tab - a legacy checkbox and an ActiveX control checkbox. The code shown above can be used to enumerte legacy checkboxes in a word document (see this article on how to create a legacy checkbox).

As far as I know, you can't use the OpenXML SDK to get/set values for an ActiveX checkbox. However you can enumerate ActiveX controls using the following code:

foreach (Control ctrl in doc.MainDocumentPart.Document.Body.Descendants<Control>())
{
   Console.Out.WriteLine(ctrl.Id);
   Console.Out.WriteLine(ctrl.Name);
   Console.Out.WriteLine(ctrl.ShapeId);
}

To determine whether or not a given Control is a checkbox you have to ckeck the class ID of the Control. The class ID of a checkbox is {8BD21D40-EC42-11CE-9E0D-00AA006002F3}. Here is a code sample to get the class ID (I don't know if there is an easier way...):

OpenXmlPart part = doc.MainDocumentPart.GetPartById(ctrl.Id);
OpenXmlReader re = OpenXmlReader.Create(part.GetStream());
re.Read();
OpenXmlElement el = re.LoadCurrentElement();          
if(el.GetAttribute("classid", el.NamespaceUri).Value == "{8BD21D40-EC42-11CE-9E0D-00AA006002F3}")
{
  Console.WriteLine("Checkbox found...");
}
re.Close();

END EDIT

EDIT 2

I didn't realize that there is a new checkbox control in Word 2010 (Thanks to Dennis Palmer).

To enumerate those new checkbox controls you can use the following code:

using (WordprocessingDocument doc = WordprocessingDocument.Open(filename, true))
{
   MainDocumentPart mp = doc.MainDocumentPart;

   foreach(SdtContentCheckBox cb in mp.Document.Body.Descendants<SdtContentCheckBox>())
   {         
     if(cb.Checked.Val == "1");
     {
       Console.Out.WriteLine("CHECKED");  
     }           
   }
}

END EDIT 2

Hope, this helps.

like image 79
Hans Avatar answered Feb 06 '23 10:02

Hans