TX Text Control .NET for Windows Forms

Conditional Instructions

Topic

How does a form reject an arrival date outside the season, accept only 2 to 14 nights or keep a drop-down list disabled until another field is filled in? In TX Text Control, conditional instructions do this: rules stored in the document that test the values of form fields and, depending on the result, enable or disable fields, set values, replace the items of a drop-down list or mark a value as invalid. The TextControl on your Windows Forms form applies them while users fill out a protected form, and your code can create, read and remove them.

What follows covers every part of a rule, in the Conditional Instruction dialog box and in C#: the comparisons of each form field type, how conditions combine, the instruction types and when they run. A complete booking request form then puts the rules together. Inserting, protecting and reading form fields is described in Form Fields and Conditional Instructions.

How Conditional Instructions Work

A conditional instruction has three parts:

  • A name. It can't be empty and must be unique in the document.
  • One or more conditions. Each condition compares the value of one form field with a comparison value, for example "the date of Arrival is on or after July 15".
  • One or more instructions. Each instruction names a form field and what happens to it, for example "mark Arrival as valid". The field can be the one the conditions test or any other form field of the document.

When the conditions are fulfilled, every instruction is carried out. An instruction can also have an else value, which is applied when the conditions are not fulfilled. The TextControl evaluates a rule at two moments:

  1. When the document becomes a form. A form is a document in the ReadAndSelect edit mode with read-only document permissions, as Let Users Fill Out the Form shows. When the document is switched to a form or loaded as one, all rules are applied once. These are the initial instructions.
  2. When the value of a form field changes that one of its conditions refers to. A check box changes when it is clicked, a date or selection form field when the user picks a date or an item, and a text, date or combo box field when the user has typed a new value and leaves the field.

Four classes describe these parts: Condition, Instruction, ConditionalInstruction and ConditionalInstructionCollection, which holds the rules of a document and is returned by FormFieldCollection.ConditionalInstructions. The sections below name each setting in the dialog box and in the class library, so that rules created in either way can be read side by side.

Turn On Form Validation

Rules are applied only while TextControl.IsFormFieldValidationEnabled is true. It is false by default. In the ribbon, the Enable Form Validation button of the Form Validation group on the Form Fields tab (the ribbon item TXITEM_EnableFormValidation of RibbonFormFieldsTab) sets the same property, and the other buttons of the group stay disabled until it is on.

While validation is off, the collection is locked: Count returns -1, ConditionalInstructionNames, GetItem and GetItems return null, and Add, Remove and Clear return false. The last three also return false while the document is protected as a form, so create and change rules before you protect it.

Create Rules in the Dialog Box

  1. Load or create the form and click Enable Form Validation.

  2. Click Manage Conditional Instructions. It shows the Conditional Instructions sidebar, which lists the rules of the document with the buttons New..., Edit... and Delete. Click New.... Sidebar and Panels describes how to connect the sidebar to the ribbon tab.

  3. The Conditional Instruction dialog box has three sections:

    • Name: the unique name of the rule.
    • Condition(s): in each row, the form field whose value is tested (a field without a name is listed under a generated unique name), the comparison operator and the comparison value. The ➕ button adds a row, and each row after the first is joined to the one before it with And or Or.
    • Perform these actions: in each row, the form field to change, the instruction and its value. The ➕ button adds a row, and the button to the left of it opens the Advanced Settings dialog box of the instruction.

    The Conditional Instruction dialog box with the name Validate Arrival, two date conditions on Arrival joined by And, the open list of comparison value kinds Date, Year, Month and Day of the month, and the action Set value as valid.

  4. Click OK. The rule is listed by its name in the sidebar, which shows its conditions and actions as sentences.

    The Conditional Instructions sidebar listing the seven rules of the booking request form with Validate Arrival selected, its two date conditions and its action as sentences, and the buttons New, Edit and Delete.

In code, ConditionalInstructionDialog opens the Conditional Instruction dialog box and ManageConditionalInstructionsDialog the list of all rules; the latter isn't opened while IsFormFieldValidationEnabled is false.

Create Rules in Code

A rule in code is a Condition array and an Instruction array under a name. The following handler in Form1.cs creates the rule Validate Arrival for a form that has a date form field named Arrival. The file needs using TXTextControl;:

private void addArrivalRuleButton_Click(object sender, EventArgs e)
{
    // Rules can only be added while validation is enabled
    textControl1.IsFormFieldValidationEnabled = true;

    DateFormField arrival = FindFormField("Arrival") as DateFormField;

    if (arrival == null)
    {
        return;
    }

    // Arrival from July 15 to November 15, 2020
    Condition fromJuly15 = new Condition(arrival,
        Condition.ComparisonOperators.IsGreaterThanOrEqual, new DateTime(2020, 7, 15));
    Condition toNovember15 = new Condition(arrival,
        Condition.ComparisonOperators.IsLessThanOrEqual, new DateTime(2020, 11, 15));

    // Valid if the conditions are fulfilled, invalid otherwise
    Instruction markValid = new Instruction(arrival,
        Instruction.InstructionTypes.IsValueValid, true, false);

    ConditionalInstruction validateArrival = new ConditionalInstruction(
        "Validate Arrival",
        new Condition[] { fromJuly15, toNovember15 },
        new Instruction[] { markValid });

    textControl1.FormFields.ConditionalInstructions.Add(validateArrival);
}

// Return the form field with the given name, or null
private FormField FindFormField(string name)
{
    foreach (FormField formField in textControl1.FormFields)
    {
        if (formField.Name == name)
        {
            return formField;
        }
    }

    return null;
}

The code works as follows:

  • The Condition constructor takes the form field to test, a value of Condition.ComparisonOperators and the comparison value. Its overloads depend on the type of the form field, as the next section shows.
  • The Instruction constructor takes the form field to change, a value of Instruction.InstructionTypes, the value to set and, as here, the else value.
  • The ConditionalInstruction constructor joins the name, the conditions and the instructions. It throws an ArgumentException when the name is empty or an array is empty or contains null. Instead of the constructor, you can create an empty object and set Name, Conditions and Instructions.
  • Add inserts the rule into the document and returns true. It throws an ArgumentException when another rule of the document has the same name. The form fields of the conditions and instructions must be part of the document at that moment.

Conditions for Each Form Field Type

The operators and values a condition offers depend on the type of the form field it tests:

Form fieldTestsOperators in the dialog boxComparison values
Check box (CheckFormField)CheckedIs, Is notChecked, Unchecked
Text box (TextFormField)TextIs, Is not, Contains, Starts with, Ends with, Does not contain, Does not start with, Does not end with, Is greater than, Is greater than or equal, Is less than, Is less than or equal, Matches regular expression, Does not match regular expressionA text, or Empty (no value set)
Drop-down list or combo box (SelectionFormField)TextAs for text boxesA text, Empty (no value set), one of the items, or A list item
Date (DateFormField)DateIs/Is in, Is not/Is not in, After, Is on or after, Before, Is on or beforeA date, a year, a month, a day of the month, a weekday, or Empty (no value set)

Empty (no value set) tests whether the field has no value: a text box or selection form field without text, or a date field without a date. It works with Is and Is not only, and not for check boxes. In code, the comparison value is null, or an empty string for text:

// Fulfilled while no number of travelers is selected
Condition noTravelers = new Condition(numberOfTravelers, Condition.ComparisonOperators.Is, null);

Check Boxes

A check box condition tests one of two states. The constructor takes the check box and a bool: true stands for Is Checked, false for Is not Checked.

Condition breakfastChecked = new Condition(breakfast, true);

A condition row of the Conditional Instruction dialog box that tests whether checkFormField1 Is Checked.

Text Boxes and Selection Form Fields

Text and selection form fields are compared by their text in three ways:

  • Text comparison. Is and Is not compare the whole text, Contains and Does not contain look for a part of it, and the Starts with and Ends with operators test the beginning and the end.
  • Greater and less. Is greater than, Is greater than or equal, Is less than and Is less than or equal compare numbers when both the text and the comparison value are numbers, decimal numbers included. Otherwise, they compare the strings in alphabetical order, case-sensitively. Numbers are read with the culture of the current thread (Thread.CurrentThread.CurrentCulture): with English settings, "1.0" is the number 1, but with German settings, where the comma separates decimals, the period is ignored and "1.0" is 10.
  • Regular expressions. Matches regular expression and Does not match regular expression test the text against a pattern of the .NET regular expression engine. In code, this is the operator Is or IsNot with the flag Regex of Condition.TextComparisonFlags; the default CustomText compares plain text. With this overload, an empty pattern throws an ArgumentException.
// Fulfilled when the text is not a whole number
Condition notWholeNumber = new Condition(numberOfNights, Condition.ComparisonOperators.IsNot,
    @"^\d+$", Condition.TextComparisonFlags.Regex);

Write patterns as verbatim strings (@"..."), so that C# doesn't read the backslashes as escape characters.

Four condition rows of the Conditional Instruction dialog box joined by And that test a text form field: Contains @, Does not start with @, Ends with .com and Does not end with @.com.

Items of Selection Form Fields

A selection form field can also be compared with its Items, with the flags of Condition.ItemComparisonFlags:

  • One of the items (SpecificItem) compares the text with that item, with any operator except the two regular expression operators. Unlike a plain text comparison, the condition is fulfilled only while the field still has an item with this text. The constructor throws an ArgumentException when the value isn't one of the items.
  • A list item (AnyItem) tests whether the text equals any of the items. It works with Is and Is not only, and the comparison value is ignored. With Is, the condition is fulfilled as soon as the user selects an item, and not fulfilled while the field is empty or shows typed text that isn't an item.
// Fulfilled when an item is selected
Condition anyItem = new Condition(numberOfTravelers, Condition.ComparisonOperators.Is,
    null, Condition.ItemComparisonFlags.AnyItem);

// Fulfilled for the item "5" and the items after it
Condition fiveOrMore = new Condition(numberOfTravelers, Condition.ComparisonOperators.IsGreaterThanOrEqual,
    "5", Condition.ItemComparisonFlags.SpecificItem);

The Conditional Instruction dialog box with the condition Number of Travelers Is and the open list of comparison values: the items 1 to 8, Empty (no value set) and A list item.

Two condition rows of the Conditional Instruction dialog box for a drop-down list: Is not the item XS, And Is greater than or equals the item XL, with the items XS to XXXL open.

Date Form Fields

A date condition compares the date of the field with one of five kinds of values, which the constructor takes as different types:

ValueType in the constructorOperators
A dateDateTimeAll six
A year from 1601 to 9999intAll six
A monthCondition.Month, such as Condition.Month.DecemberAll six
A day of the month from 1 to 31intAll six
A weekdaySystem.DayOfWeekIs, Is not

An int outside both ranges throws an ArgumentException. After and Is on or after are IsGreaterThan and IsGreaterThanOrEqual, and Before and Is on or before are IsLessThan and IsLessThanOrEqual. Condition.ComparisonValueType returns how a condition interprets its value, as a value of Condition.ComparisonValueTypes.

// The last Monday of May
Condition monday = new Condition(date, Condition.ComparisonOperators.Is, DayOfWeek.Monday);
Condition may = new Condition(date, Condition.ComparisonOperators.Is, Condition.Month.May);
Condition lastWeek = new Condition(date, Condition.ComparisonOperators.IsGreaterThanOrEqual, 25);

Three condition rows of the Conditional Instruction dialog box joined by And that test a date form field: the weekday Is Monday, the month Is May and the day of the month Is on or after 25.

Combine Conditions With And and Or

Each condition after the first is joined to the one before it by its LogicalConnective, And or Or of Condition.LogicalConnectives. The default is And, and the connective of the first condition is ignored. The conditions are tested in the order of the array.

And binds more strongly than Or. Conditions joined by And form a group that is fulfilled when all of its conditions are, and Or joins such groups:

C1 And C2 Or C3         means   (C1 And C2) Or C3
C1 Or C2 And C3         means   C1 Or (C2 And C3)

Three check box conditions in the Conditional Instruction dialog box, checkFormField1 Is Checked, Or checkFormField2 Is Checked, And checkFormField3 Is Checked, with parentheses around the last two conditions.

There are no parentheses. To test C1 And (C2 Or C3), repeat the shared condition in each group:

C1 And C2 Or C1 And C3  means   (C1 And C2) Or (C1 And C3)

In code, the same Condition object may appear several times in the array. Give it the connective Or and put it first: the first occurrence ignores its connective, and every later one starts a new group. Rule 7 of the booking form below is built this way.

Instruction Types

An instruction changes one form field. Its type and value decide what happens:

In the dialog boxInstructionTypesValueForm fields
Allow editing, Deny editingIsEnabledbool, the new value of FormField.EnabledAll
Set value as valid, Set value as invalidIsValueValidbool, true for validAll
Set new valueSetValuebool for a check box (Checked or Unchecked), a string for text and selection form fields, a DateTime for a date field; null or Empty (no value set) removes the value of all but check boxesAll
Set new itemsSetItemsstring[] that replaces the Items of the field; null or an empty array removes all itemsSelection form fields

Three Set new value instructions in the Conditional Instruction dialog box: a combo box set to Empty (no value set), a drop-down list set to Product Number and a second drop-down list whose open list of items has TX Spell selected.

Set new value can set a selection form field to one of its items, or a combo box to any text. In the dialog box, Specify items to set... next to Set new items opens the Edit Items dialog box, in which you enter the new items. An item can't be an empty string, and removing all items from the list creates an instruction that empties the drop-down list.

The Conditional Instruction dialog box with the instruction Set new items for comboBox and the Edit Items dialog box that lists Text Control, TX Spell and TX Barcode.

// Replace the items of a combo box
Instruction setProducts = new Instruction(comboBox, Instruction.InstructionTypes.SetItems,
    new string[] { "Text Control", "TX Spell", "TX Barcode" });

A rule can change several fields, and one field several times, but not twice with the same instruction type: the ConditionalInstruction constructor and its Instructions property throw an ArgumentException for two instructions of the same type for the same form field.

Else Instructions

An else instruction sets a value when the conditions of the rule are not fulfilled. The dialog box and the class library differ here:

  • The dialog box adds an else instruction with the opposite value to Allow editing, Deny editing, Set value as valid and Set value as invalid. A rule that marks a field as valid therefore marks it as invalid when its conditions are not met. Set new value and Set new items get no else instruction by themselves.
  • The class library never adds one. The constructor with four parameters sets the else value and turns IsElseInstructionEnabled on; with three parameters, it's off. Setting IsElseInstructionEnabled to false turns it off again.

In the Advanced Settings dialog box of an instruction, the check box In case the condition is not fulfilled, set the value to turns the else instruction on or off, and the value next to it sets it. ElseInstructionValue returns the else value. When none was set, it returns a default: the negation of the value for IsEnabled, IsValueValid and check boxes, an empty string for text and selection form fields, null for date fields and an empty array for SetItems.

The Advanced Settings dialog box of an instruction with the check boxes Is initial instruction, Perform instruction on form field value changes and In case the condition is not fulfilled, set the value to, and the value Set value as valid.

Initial and Change Instructions

By default, an instruction runs at both moments: when the document becomes a form, and when a tested field changes. Two properties, both true by default, switch one of the moments off for a single instruction:

PropertyCheck box in Advanced SettingsSet to false when
IsInitialInstructionIs initial instructionThe instruction must not run when the form opens, for example because its conditions are fulfilled by the empty form.
IsFormFieldChangeInstructionPerform instruction on form field value changesThe instruction must run only when the form opens and ignore what users enter.

Both are set on the Instruction before the rule is added, for example with an object initializer: new Instruction(field, Instruction.InstructionTypes.IsValueValid, false) { IsFormFieldChangeInstruction = false }.

Highlight and Check Invalid Values

An instruction of the type IsValueValid marks a form field value as valid or invalid; the value itself doesn't change. In the protected form, the Highlight Invalid Values button of the Form Validation group (TXITEM_HighlightInvalidValues) highlights the invalid values in yellow, and Previous Invalid Value and Next Invalid Value move to them. Turn highlighting on before you protect the form.

The reference documents ConditionalInstructionCollection.IsValueValid for asking whether a rule has marked the value of a form field as invalid, but the assemblies of version 34.0 SP5 don't contain the method: code that calls it doesn't compile. Highlight Invalid Values therefore is the way to show which values a rule has marked, and your code reads the values themselves, as Read the Values shows.

Manage the Rules in Code

ConditionalInstructionCollection contains the rules of the document. All of its members require IsFormFieldValidationEnabled, and the members that change rules also require an unprotected document:

MemberWhat it does
AddAdds a rule to the collection and the document
RemoveRemoves the rule with the given name, and returns false if there is none
ClearRemoves all rules
GetItemReturns the rule with the given name
GetItemsReturns the rules whose conditions or instructions refer to the given form field
ConditionalInstructionNamesReturns the names of all rules
CountReturns the number of rules

The following handler lists the rules that involve the form field at the input position, which FormFieldCollection.GetItem returns, for example before the user deletes the field:

private void showRulesButton_Click(object sender, EventArgs e)
{
    textControl1.IsFormFieldValidationEnabled = true;

    FormField formField = textControl1.FormFields.GetItem();

    if (formField == null)
    {
        return;
    }

    ConditionalInstruction[] rules = textControl1.FormFields.ConditionalInstructions.GetItems(formField);
    string names = "";

    foreach (ConditionalInstruction rule in rules)
    {
        names += rule.Name + "\n";
    }

    MessageBox.Show(this, names == "" ? "No rules use this field." : names);
}

Example: A Booking Request Form

The TX Text Control Words sample Conditional Instructions, described in Form Fields and Conditional Instructions, loads a booking request form with seven rules. The version below validates dates of the year 2020. The form has four form fields:

NameTypeContent
ArrivalDate form fieldThe date of arrival
Number of NightsText form fieldThe length of the stay
Number of TravelersSelection form field, drop-down listThe items 1 to 8
Desired AccommodationSelection form field, drop-down listThe accommodation

The booking request form template with the fields Arrival, Number of Nights, Number of Travelers and Desired Accommodation, the notes on the allowed values next to them, and its 2020 date range.

The form must behave as follows:

  • Every field is required. A field without a value is invalid.
  • Arrival is valid from July 15 to November 15, 2020.
  • Number of Nights accepts whole numbers from 2 to 14.
  • Desired Accommodation is disabled until a number of travelers is selected. It is invalid only while it is enabled and empty.
  • Groups of 5 or more can choose Apartment Belle View and Ocean View Suite. Groups of up to 4 can also choose King's Life Suite and Tree House Room. If the group grows to 5 or more after one of these two was chosen, the choice is removed.

The Seven Rules

Each rule is listed with its settings in the Conditional Instruction dialog box:

  1. Validate Arrival. Arrival Is on or after 7/15/2020 And Arrival Is on or before 11/15/2020; Arrival Set value as valid, with the else instruction Set value as invalid that the dialog box adds. A missing date doesn't fulfill the conditions, so the else instruction marks it as invalid too.
  2. Validate Number of Nights. Number of Nights Is less than 2 Or Is greater than 14 Or Does not match regular expression ^\d+$; Number of Nights Set value as invalid, else valid. The rule tests for invalid values: the pattern accepts only digits, so an empty field, a decimal number or text is invalid, and one of the three conditions is enough.
  3. Validate Number of Travelers. Number of Travelers Is Empty (no value set); Number of Travelers Set value as invalid, else valid. The condition Number of Travelers Is A list item with Set value as valid does the same from the other side.
  4. Validate Desired Accommodation. Number of Travelers Is A list item And Desired Accommodation Is Empty (no value set); Desired Accommodation Set value as invalid, else valid. Two fields take part, so the rule is evaluated when either of them changes.
  5. Enable/Disable Desired Accommodation. Number of Travelers Is Empty (no value set); Desired Accommodation Deny editing, else Allow editing.
  6. Update Desired Accommodation Items. Number of Travelers Is greater than or equal the item 5; Desired Accommodation Set new items Apartment Belle View and Ocean View Suite. In Advanced Settings, the else instruction is turned on and sets all four accommodations. The items are numbers, so they are compared as numbers.
  7. Reset Desired Accommodation. Number of Travelers Is greater than or equal the item 5 And Desired Accommodation Is King's Life Suite Or Number of Travelers Is greater than or equal the item 5 And Desired Accommodation Is Tree House Room; Desired Accommodation Set new value Empty (no value set), without an else instruction. Because And binds more strongly than Or, the travelers condition is repeated in both groups.

The Conditional Instruction dialog box with the rule Validate Number of Nights: Is less than 2, Or Is greater than 14, Or Does not match regular expression ^\d+$, and the open list of instructions with Set value as invalid selected.

The Conditional Instruction dialog box with the rule Validate Number of Travelers: Number of Travelers Is Empty (no value set), and the instruction Set value as invalid.

The Conditional Instruction dialog box with the rule Validate Desired Accommodation: Number of Travelers Is A list item, And Desired Accommodation Is Empty (no value set), and the instruction Set value as invalid.

The Conditional Instruction dialog box with the rule Enable/Disable Desired Accommodation: Number of Travelers Is Empty (no value set), and the instruction Deny editing for Desired Accommodation.

The Conditional Instruction dialog box with the rule Update Desired Accommodation Items, the condition Number of Travelers Is greater than or equals 5, and the Edit Items dialog box with Apartment Belle View and Ocean View Suite.

The Conditional Instruction dialog box with the rule Reset Desired Accommodation: two groups in parentheses joined by Or, each Number of Travelers Is greater than or equals 5 And Desired Accommodation Is King's Life Suite or Tree House Room, and the instruction Set new value Empty (no value set).

The Rules in Code

The following handler creates all seven rules for a document that contains the four form fields and no rules yet. It uses the FindFormField method of Create Rules in Code and protects the form at the end:

private void addBookingRulesButton_Click(object sender, EventArgs e)
{
    textControl1.IsFormFieldValidationEnabled = true;

    DateFormField arrival = FindFormField("Arrival") as DateFormField;
    TextFormField numberOfNights = FindFormField("Number of Nights") as TextFormField;
    SelectionFormField numberOfTravelers = FindFormField("Number of Travelers") as SelectionFormField;
    SelectionFormField desiredAccommodation = FindFormField("Desired Accommodation") as SelectionFormField;

    if (arrival == null || numberOfNights == null || numberOfTravelers == null || desiredAccommodation == null)
    {
        return;
    }

    ConditionalInstructionCollection rules = textControl1.FormFields.ConditionalInstructions;

    // 1. Validate Arrival
    rules.Add(new ConditionalInstruction("Validate Arrival",
        new Condition[]
        {
            new Condition(arrival, Condition.ComparisonOperators.IsGreaterThanOrEqual, new DateTime(2020, 7, 15)),
            new Condition(arrival, Condition.ComparisonOperators.IsLessThanOrEqual, new DateTime(2020, 11, 15))
        },
        new Instruction[] { new Instruction(arrival, Instruction.InstructionTypes.IsValueValid, true, false) }));

    // 2. Validate Number of Nights
    rules.Add(new ConditionalInstruction("Validate Number of Nights",
        new Condition[]
        {
            new Condition(numberOfNights, Condition.ComparisonOperators.IsLessThan, "2"),
            new Condition(numberOfNights, Condition.ComparisonOperators.IsGreaterThan, "14")
                { LogicalConnective = Condition.LogicalConnectives.Or },
            new Condition(numberOfNights, Condition.ComparisonOperators.IsNot, @"^\d+$", Condition.TextComparisonFlags.Regex)
                { LogicalConnective = Condition.LogicalConnectives.Or }
        },
        new Instruction[] { new Instruction(numberOfNights, Instruction.InstructionTypes.IsValueValid, false, true) }));

    // 3. Validate Number of Travelers
    Condition noTravelers = new Condition(numberOfTravelers, Condition.ComparisonOperators.Is, null);

    rules.Add(new ConditionalInstruction("Validate Number of Travelers",
        new Condition[] { noTravelers },
        new Instruction[] { new Instruction(numberOfTravelers, Instruction.InstructionTypes.IsValueValid, false, true) }));

    // 4. Validate Desired Accommodation
    rules.Add(new ConditionalInstruction("Validate Desired Accommodation",
        new Condition[]
        {
            new Condition(numberOfTravelers, Condition.ComparisonOperators.Is, null, Condition.ItemComparisonFlags.AnyItem),
            new Condition(desiredAccommodation, Condition.ComparisonOperators.Is, null)
        },
        new Instruction[] { new Instruction(desiredAccommodation, Instruction.InstructionTypes.IsValueValid, false, true) }));

    // 5. Enable/Disable Desired Accommodation
    rules.Add(new ConditionalInstruction("Enable/Disable Desired Accommodation",
        new Condition[] { noTravelers },
        new Instruction[] { new Instruction(desiredAccommodation, Instruction.InstructionTypes.IsEnabled, false, true) }));

    // 6. Update Desired Accommodation Items
    Condition fiveOrMore = new Condition(numberOfTravelers, Condition.ComparisonOperators.IsGreaterThanOrEqual,
        "5", Condition.ItemComparisonFlags.SpecificItem)
    {
        LogicalConnective = Condition.LogicalConnectives.Or
    };

    rules.Add(new ConditionalInstruction("Update Desired Accommodation Items",
        new Condition[] { fiveOrMore },
        new Instruction[]
        {
            new Instruction(desiredAccommodation, Instruction.InstructionTypes.SetItems,
                new string[] { "Apartment Belle View", "Ocean View Suite" },
                new string[] { "King's Life Suite", "Tree House Room", "Apartment Belle View", "Ocean View Suite" })
        }));

    // 7. Reset Desired Accommodation: (5 or more And King's Life Suite) Or (5 or more And Tree House Room)
    Condition kingsLifeSuite = new Condition(desiredAccommodation, Condition.ComparisonOperators.Is, "King's Life Suite");
    Condition treeHouseRoom = new Condition(desiredAccommodation, Condition.ComparisonOperators.Is, "Tree House Room");

    rules.Add(new ConditionalInstruction("Reset Desired Accommodation",
        new Condition[] { fiveOrMore, kingsLifeSuite, fiveOrMore, treeHouseRoom },
        new Instruction[] { new Instruction(desiredAccommodation, Instruction.InstructionTypes.SetValue, null) }));

    // Protect the form: the initial instructions run now
    textControl1.DocumentPermissions.AllowEditingFormFields = true;
    textControl1.DocumentPermissions.ReadOnly = true;
    textControl1.EditMode = EditMode.ReadAndSelect;
}

The code works as follows:

  • The same Condition objects are used in more than one rule and, in rule 7, twice in one array. fiveOrMore has the connective Or: rule 6 ignores it because the condition is the first of its array, and in rule 7 it opens the second group at the third position.
  • Rule 6 compares with the item "5" (SpecificItem), but the accommodation conditions of rule 7 compare plain text. Once rule 6 has replaced the items, King's Life Suite is no longer one of them, and a condition on that item would never be fulfilled.
  • Rule 7 has no else value, so the choice is only removed and never set back.
  • Setting ReadOnly, AllowEditingFormFields and EditMode makes the document a form and runs the initial instructions. After that, Add returns false.

The Form in Use

When the form is protected, the initial instructions run. Arrival, Number of Nights and Number of Travelers are marked as invalid because they are empty. Desired Accommodation is disabled by rule 5, but not marked as invalid, because rule 4 requires a selected number of travelers:

The protected booking request form with Highlight Invalid Values on: Arrival, Number of Nights and Number of Travelers highlighted in yellow as invalid, and Desired Accommodation disabled.

As the user fills out the form, the rules follow each change. A date before July 15 keeps Arrival invalid, and "2.5" nights stays invalid because of the regular expression. Selecting 3 travelers enables Desired Accommodation and marks it as invalid until an accommodation is chosen. If the user then changes the number of travelers from 3 to 6 after choosing King's Life Suite, rule 7 empties Desired Accommodation, rule 4 marks it as invalid and rule 6 leaves only the two accommodations for larger groups.

The protected booking request form with the arrival 7/14/2020, 2.5 nights, 3 travelers and King's Life Suite, the invalid date and number of nights highlighted.

The same form after the number of travelers was changed to 6: Desired Accommodation is empty and highlighted as invalid.

The booking request form filled out with valid values: the arrival 8/20/2020, 12 nights, 6 travelers and Ocean View Suite.

More Condition Examples

GoalConditions
Check box 1 is checkedcb1 Is Checked (new Condition(cb1, true))
Both check boxes are uncheckedcb1 Is not Checked And cb2 Is not Checked
At least one check box is checkedcb1 Is Checked Or cb2 Is Checked
Check box 1, or both 2 and 3, are checkedcb1 Is Checked Or cb2 Is Checked And cb3 Is Checked
A text box isn't emptyIs not Empty (no value set)
The state ColoradoIs "Colorado" Or Is "CO"
A simple .com e-mail addressContains "@" And Does not start with "@" And Ends with ".com" And Does not end with "@.com"
A positive numberIs greater than "0"
A number from 0 to 18Is greater than or equal "0" And Is less than or equal "18"
Text starting with a, b or c in either caseIs greater than or equal "a" And Is less than "d" Or Is greater than or equal "A" And Is less than "D"
5 to 8 charactersMatches regular expression ^.{5,8}$
No digits and no white spaceDoes not match regular expression [\s\d]+
The item "Economy class basic" is selectedIs the item "Economy class basic"
A subcategory of the item "Economy class"Is not the item "Economy class" And Contains the item "Economy class"
A clothing size from XL to XXXLIs not the item "XS" And Is greater than or equal the item "XL"
The text of a combo box is one of its itemsIs A list item
No dateIs Empty (no value set)
Any date but March 11, 2027Is not 3/11/2027
Before March 11, 2027Before 3/11/2027
From December 24, 2027 to January 1, 2028Is on or after 12/24/2027 And Is on or before 1/1/2028
December 25 of any yearIs December And Is day 25
The fourth Thursday of NovemberIs November And Is Thursday And Is on or after day 22 And Is on or before day 28
The last Monday of MayIs Monday And Is May And Is on or after day 25
A weekendIs Saturday Or Is Sunday
A working dayIs not Saturday And Is not Sunday
Every Monday of 2027Is year 2027 And Is Monday
Every weekend of 2027Is year 2027 And Is Saturday Or Is year 2027 And Is Sunday

The last week of a month starts on the 25th in months with 31 days, on the 24th in months with 30 days and on the 22nd in February, or the 23rd in a leap year. Comparisons of text are case-sensitive, which is why the a-to-c example needs a group for each case. In code, "the item" is the SpecificItem flag, a day or a year is an int, and a month a Condition.Month value:

// December 25 of any year
Condition december = new Condition(date, Condition.ComparisonOperators.Is, Condition.Month.December);
Condition day25 = new Condition(date, Condition.ComparisonOperators.Is, 25);

// A subcategory of the item "Economy class"
Condition notEconomy = new Condition(flightClass, Condition.ComparisonOperators.IsNot,
    "Economy class", Condition.ItemComparisonFlags.SpecificItem);
Condition containsEconomy = new Condition(flightClass, Condition.ComparisonOperators.Contains,
    "Economy class", Condition.ItemComparisonFlags.SpecificItem);

Regular expressions for common values, for Matches regular expression:

Credit card number          ^((4\d{3})|(5[1-5]\d{2})|(6011))-?\d{4}-?\d{4}-?\d{4}|3[4,7]\d{13}$
E-mail address              ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
US social security number   ^\d{3}-\d{2}-\d{4}$
US telephone number         ^[2-9]\d{2}-\d{3}-\d{4}$
US ZIP code                 ^(?!0{5})(\d{5})(?!-?0{4})(|-\d{4})?$

More Instruction Examples

Reset a field. To disable a field, remove its value and clear an invalid mark when a condition is met, use three instructions for the field: Deny editing with its else instruction Allow editing; Set new value Empty (no value set); and Set value as valid with its else instruction turned off in Advanced Settings, because another rule decides when the field is invalid. Clear Is initial instruction for all three, as the reset isn't needed when the form opens. A check box can't be empty, so its reset value is Checked or Unchecked.

Instruction[] resetInstructions = new Instruction[]
{
    new Instruction(textFormField, Instruction.InstructionTypes.IsEnabled, false, true) { IsInitialInstruction = false },
    new Instruction(textFormField, Instruction.InstructionTypes.SetValue, null) { IsInitialInstruction = false },
    new Instruction(textFormField, Instruction.InstructionTypes.IsValueValid, true) { IsInitialInstruction = false }
};

Three instructions for textFormField in the dialog box, Deny editing, Set new value Empty (no value set) and Set value as valid, and the Advanced Settings of the second with Is initial instruction and the else instruction cleared.

Mark an empty combo box as invalid when the form opens. Choose a condition that the new form fulfills, such as the combo box Is Empty (no value set), and the instruction Set value as invalid without an else instruction. Clear Perform instruction on form field value changes, so that the instruction doesn't override another rule that validates what the user enters:

Instruction invalidOnOpen = new Instruction(comboBox, Instruction.InstructionTypes.IsValueValid, false)
{
    IsFormFieldChangeInstruction = false
};

The instruction Set value as invalid for comboBox with its Advanced Settings: Is initial instruction checked, Perform instruction on form field value changes and the else instruction cleared.

Radio Button Groups

In a radio button group, users select exactly one option. TX Text Control builds it from check boxes and conditional instructions, in four steps:

  1. Give the check boxes the look of radio buttons. Select a check box, and choose the symbols in the Checked Symbol and Unchecked Symbol lists of the Check Box Properties group on the Form Fields tab. In code, set CheckedCharacter and UncheckedCharacter.

    TX Text Control Words with the Form Fields tab, two check boxes named RadioButton1 and RadioButton2, and the open list of Checked Symbols of the Check Box Properties group.

  2. Uncheck all buttons, so that no option is selected when the form opens: click the checked ones, or set Checked to false.

  3. Uncheck the others when a button is checked. For each button, a rule with the condition "button Is Checked" sets every other button to Unchecked with Set new value.

  4. Keep a checked button from being unchecked. For each button, a rule with the condition "button Is not Checked" sets the same button back to Checked, with Is initial instruction cleared. When the form opens, all buttons are unchecked, and as initial instructions these rules would check every button, so the button handled last would appear selected.

The Conditional Instruction dialog box with the rule Radio Button 1 Checked: radioButton1 Is Checked, and the instruction Set new value Unchecked for radioButton2.

The rule Radio Button 1 Unchecked: radioButton1 Is not Checked, the instruction Set new value Checked for radioButton1, and its Advanced Settings with Is initial instruction cleared.

The following methods do all four steps for the check boxes of a group:

private void MakeRadioButtonGroup(CheckFormField[] buttons)
{
    textControl1.IsFormFieldValidationEnabled = true;

    foreach (CheckFormField button in buttons)
    {
        button.CheckedCharacter = '◉';     // fisheye
        button.UncheckedCharacter = '⭕';   // large circle
        button.Checked = false;
    }

    for (int i = 0; i < buttons.Length; i++)
    {
        AddRadioButtonRules(buttons, i);
    }
}

private void AddRadioButtonRules(CheckFormField[] buttons, int index)
{
    CheckFormField button = buttons[index];

    // When this button is checked, uncheck all others
    List<Instruction> uncheckOthers = new List<Instruction>();

    foreach (CheckFormField other in buttons)
    {
        if (other != button)
        {
            uncheckOthers.Add(new Instruction(other, Instruction.InstructionTypes.SetValue, false));
        }
    }

    textControl1.FormFields.ConditionalInstructions.Add(new ConditionalInstruction(
        button.Name + " Checked",
        new Condition[] { new Condition(button, true) },
        uncheckOthers.ToArray()));

    // When this button is unchecked by a click, check it again
    textControl1.FormFields.ConditionalInstructions.Add(new ConditionalInstruction(
        button.Name + " Unchecked",
        new Condition[] { new Condition(button, false) },
        new Instruction[]
        {
            new Instruction(button, Instruction.InstructionTypes.SetValue, true) { IsInitialInstruction = false }
        }));
}

List<Instruction> needs using System.Collections.Generic;. The check boxes must have unique names, because the names of the rules are made from them. Call MakeRadioButtonGroup before the document is protected.

Next Steps