Editing PDF form fields
Use form field editing to control validation and behavior in PDF workflows.
Common use cases include:
- Setting fields to read-only after approval
- Marking fields as required for validation
- Reading default values for reset logic
- Traversing parent-child field hierarchies
- Accessing widgets for appearance updates
How Nutrient helps
Nutrient .NET SDK handles form field dictionaries, property updates, and hierarchy traversal.
The SDK handles:
- PDF form field dictionaries and annotation structures
- Recursive hierarchy traversal for parent-child fields
- Flag manipulation at the byte level
- Low-level widget access for appearance customization
Complete implementation
This example edits multiple form field properties in one pass:
using Nutrient;Opening a document with form fields
Open the PDF with a using statement(opens in a new tab), create an editor, and use the FormFieldCollection property to access the document’s form fields. The collection provides lookup by fully qualified name with FindByFullName(), which returns null when no field has that name. This sample works through a list of known field names — inspect your own document’s names first and adjust the list:
string[] fieldNames = ["Text1", "Check1", "RadioGroup1", "Dropdown1", "List1", "Signature1", "Button1"];
try{ using Document document = Document.Open("input_forms.pdf"); using PdfEditor editor = PdfEditor.Edit(document); var formFields = editor.FormFieldCollection; Console.WriteLine($"The document contains {formFields.Count} form fields");Inspecting field properties
Look up each field and inspect key properties.
This sample reads:
- Field name and full name
- Field type
- Terminal/parent state
- Read-only and required flags
- Current value and default value
Use this step for form auditing and validation setup.
foreach (var fullName in fieldNames) { var field = formFields.FindByFullName(fullName); if (field == null) { Console.WriteLine($"No field named {fullName}"); continue; } Console.WriteLine($"Name: {field.Name}"); Console.WriteLine($"FullName: {field.FullName}"); Console.WriteLine($"FieldType: {field.FieldType}"); Console.WriteLine($"IsTerminal: {field.IsTerminal}"); Console.WriteLine($"IsReadOnly: {field.IsReadOnly}"); Console.WriteLine($"IsRequired: {field.IsRequired}"); Console.WriteLine($"Value: {field.Value}"); Console.WriteLine($"DefaultValue: {field.DefaultValue}"); Console.WriteLine("---"); }Making a field read-only
Find a field by full name and set it to read-only.
In this sample, Text1 is locked by setting IsReadOnly to true. If a name doesn’t exist in your input file, FindByFullName() returns null and the sample skips the update — adjust the names to match your document:
var textField = formFields.FindByFullName("Text1"); if (textField != null) { textField.IsReadOnly = true; }Making a field required
Find a field by full name and mark it as required.
In this sample, Check1 is marked required by setting IsRequired to true:
var checkField = formFields.FindByFullName("Check1"); if (checkField != null) { checkField.IsRequired = true; }Reading a default value
Read a field’s default value for reset and change-tracking logic.
In this sample:
Valuereturns the current value.DefaultValuereturns the reset value.
var countryField = formFields.FindByFullName("Dropdown1"); if (countryField != null) { string defaultValue = countryField.DefaultValue; Console.WriteLine($"Default value: {defaultValue}"); }Working with field hierarchy
Traverse field hierarchies by checking whether a field is terminal.
For non-terminal fields, use:
ChildCountto read the child count.GetChild(i)to access each child.
Full names often use dot notation, for example Address.Street:
foreach (var fullName in fieldNames) { var field = formFields.FindByFullName(fullName); if (field != null && !field.IsTerminal) { int childCount = field.ChildCount; Console.WriteLine($"Parent field '{field.Name}' has {childCount} children:"); for (int i = 0; i < childCount; i++) { var child = field.GetChild(i); Console.WriteLine($" - Child: {child.Name}"); } } }Accessing widget annotations
Access widgets for terminal fields to inspect or update visual presentation.
In this sample:
WidgetCountreturns the number of widgets.GetWidget(i)returns a widget by index.
A field can have one or more widgets across pages:
foreach (var fullName in fieldNames) { var field = formFields.FindByFullName(fullName); if (field != null && field.IsTerminal) { int widgetCount = field.WidgetCount; Console.WriteLine($"Field '{field.Name}' has {widgetCount} widget(s)"); for (int i = 0; i < widgetCount; i++) { var widget = field.GetWidget(i); // Access widget properties for appearance customization } } }Batch updating field properties
Apply updates to multiple fields using type-based conditions.
In this sample:
- Text fields are marked required.
- Signature fields are set to read-only.
Use this pattern for workflow-driven form rules:
Compare
FieldTypedirectly against enum members (for example,PdfFormFieldType.TextandPdfFormFieldType.Signature).
foreach (var fullName in fieldNames) { var field = formFields.FindByFullName(fullName); if (field != null && field.IsTerminal) { // Make all text fields required if (field.FieldType == PdfFormFieldType.Text) { field.IsRequired = true; }
// Make all signature fields read-only until other fields are filled if (field.FieldType == PdfFormFieldType.Signature) { field.IsReadOnly = true; } } }Saving the modified form
Save the output PDF. The catch block handles NutrientException failures, and the using statements release the editor and document handles:
editor.SaveAs("output.pdf"); Console.WriteLine("Successfully edited form fields");}catch (NutrientException e){ Console.Error.WriteLine($"Error: {e.Message}"); Environment.Exit(1);}Conclusion
Use this workflow to edit form field properties:
- Open the document, create an editor, and access the form field collection with the
FormFieldCollectionproperty. - The form field collection provides lookup by fully qualified name for all fields in the document.
- Inspect field properties with
Name,FullName,FieldType,IsTerminal,IsReadOnly,IsRequired,Value, andDefaultValue. - Fully qualified names use dot-notation for hierarchical fields (e.g.
"Address.Street"). - Find fields with
FindByFullName()and setIsReadOnlytotrueto lock fields after workflow milestones. - Mark fields as required by setting
IsRequiredtotrueto enforce validation, preventing form submission until values are provided. - Read default values with the
DefaultValueproperty for reset operations and change detection. - Navigate field hierarchy with
IsTerminal,ChildCount, andGetChild(i)for parent-child relationships. - Access widget annotations with
WidgetCountandGetWidget(i)for appearance customization. - Batch update properties by looking up fields and applying conditional logic based on
FieldTypecomparisons. - Save the document with
SaveAs()to persist all property modifications.
Sample field names such as
Text1,Check1, andDropdown1match the sample input form. If those names don’t exist in your input PDF, accessing members of the looked-up field throws aNutrientException— inspect your document’s field names first and update the list accordingly.
For related form workflows, refer to the .NET SDK editor guides.