# Fields, Hyperlinks, and Bookmarks

> Use TextField, MS Word ApplicationField, HypertextLink, DocumentTarget bookmarks and DocumentLink in a Windows Forms TextControl and handle link clicks in C#.

- **Product:** TX Text Control .NET for Windows Forms
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for Windows Forms > Document Editor > Fields, Hyperlinks, and Bookmarks
- **9866 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/fields/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/fields/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/fields/llms-full.txt

---

Fields mark pieces of text that an application fills, evaluates or links: a customer name taken from a database, a merge field of a Word template, a link to a website or a jump to another place in the same document. A Windows Forms application works with all of them through collections of the `TextControl`, and users insert hyperlinks and bookmarks with the ribbon.

The examples extend the application of [Create Your First Windows Forms Application](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/getting-started/document-editor/first-application/llms-full.txt). *Form1.cs* needs `using TXTextControl;`, and each handler belongs to a button that you add in the designer.

### Kinds of Fields

A field is a marked piece of text with a name, an ID and events. The base class is [TextField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/llms-full.txt), and each kind of field has its own collection:

| Class | Represents | Collection |
|---|---|---|
| [TextField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/llms-full.txt) | A general field, for example for a database value | [TextControl.TextFields](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textfields-property/llms-full.txt) |
| [ApplicationField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/applicationfield/llms-full.txt) | A field of MS Word, such as `MERGEFIELD` or `DATE` | [TextControl.ApplicationFields](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/applicationfields-property/llms-full.txt) |
| [HypertextLink](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlink/llms-full.txt) | A link to a target outside the document, such as a URL | [TextControl.HypertextLinks](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/hypertextlinks-property/llms-full.txt) |
| [DocumentTarget](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documenttarget/llms-full.txt) | A position in the document, called a bookmark in word processors and an anchor in HTML | [TextControl.DocumentTargets](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/documenttargets-property/llms-full.txt) |
| [DocumentLink](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documentlink/llms-full.txt) | A link to a document target in the same document | [TextControl.DocumentLinks](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/documentlinks-property/llms-full.txt) |

Two more kinds have pages of their own: form fields, which users fill out, on [Form Fields and Conditional Instructions](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/form-fields/llms-full.txt), and [PageNumberField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/pagenumberfield/llms-full.txt), which exists only in headers and footers, on [Headers and Footers](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/headers-footers/llms-full.txt). The properties of the `TextControl` return the fields of the text part with the input focus: the main text, a header or footer, or a text frame. To reach the fields of all text parts, loop through [TextParts](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textparts-property/llms-full.txt), as [Fields and Subtext Parts](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-processing/fields/#find-the-fields-of-all-text-parts) shows.

### Insert and Fill Text Fields

A new field is inserted at the input position. The following handlers insert a field for a customer name and later replace its text:

```csharp
private void insertFieldButton_Click(object sender, EventArgs e)
{
    TextField customerField = new TextField("Customer name")
    {
        Name = "CustomerName",
        ID = 100,
        HighlightMode = HighlightMode.Activated,
        // Let users type in front of and behind the field
        DoubledInputPosition = true
    };

    if (textControl1.TextFields.CanAdd)
    {
        textControl1.TextFields.Add(customerField);
    }
}

private void fillFieldsButton_Click(object sender, EventArgs e)
{
    foreach (TextField field in textControl1.TextFields)
    {
        if (field.Name == "CustomerName")
        {
            field.Text = "Paul Paulsen";
        }
    }
}
```

The code works as follows:

- The [TextField constructor](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/constructor/llms-full.txt) takes the visible text. [Name](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/name-property/llms-full.txt) and [ID](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/id-property/llms-full.txt) identify the field in your code; they are saved with the document in the TX Text Control format (`StreamType.InternalUnicodeFormat`) and copied with it through the clipboard. `Name` takes any string, and its length isn't limited, so a field can carry a larger piece of text, such as data of your application.
- [HighlightMode](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/highlightmode-property/llms-full.txt) shows the field with a gray background: `Activated` while the input position is in it, `Always`, or `Never`, the default.
- [DoubledInputPosition](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/doubledinputposition-property/llms-full.txt) adds a second input position at the beginning and the end of the field. With the arrow keys, users decide whether the text they type belongs to the field or not.
- [CanAdd](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfieldcollectionbase/canadd-property/llms-full.txt) is `false` where no field can be inserted, for example inside another field. [TextFieldCollection.Add](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfieldcollection/add-method/llms-full.txt) inserts the field and returns `true` if it was inserted. The input position moves behind the inserted field.
- [Text](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/text-property/llms-full.txt) reads and replaces the text of the field.

Users can edit the text of a field like any other text. [Editable](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/editable-property/llms-full.txt) set to `false` protects the text, and [Deleteable](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/deleteable-property/llms-full.txt) set to `false` prevents that the field is deleted. When the mouse pointer is over a field, it changes to the cursor set with [FieldCursor](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/fieldcursor-property/llms-full.txt).

### Find, List and Remove Fields

```csharp
private void removeFieldButton_Click(object sender, EventArgs e)
{
    TextField field = textControl1.TextFields.GetItem(100);

    if (field != null)
    {
        // true: keep the text of the field in the document
        textControl1.TextFields.Remove(field, true);
    }
}

private void listAllFieldsButton_Click(object sender, EventArgs e)
{
    StringBuilder list = new StringBuilder();

    foreach (TextField field in textControl1.GetTextFields(TextFieldType.All))
    {
        list.AppendLine(field.GetType().Name + " " + field.Name + ": " + field.Text);
    }

    MessageBox.Show(list.ToString());
}
```

`StringBuilder` needs `using System.Text;`.

- [GetItem](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfieldcollection/getitem-method/llms-full.txt) returns the field at the input position, or the field with an ID. [Remove](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfieldcollection/remove-method/llms-full.txt) deletes a field; with `true` as the second parameter, its text stays in the document as normal text.
- [GetTextFields](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/gettextfields-method/llms-full.txt) returns the fields of one or more kinds, selected with [TextFieldType](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfieldtype/enumeration/llms-full.txt) values such as `TextField`, `ApplicationField`, `HypertextLink`, `FormFields` or `All`.

`TextControl` raises events while users work with fields. All of them pass a [TextFieldEventArgs](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfieldeventargs/llms-full.txt) with the field in its `TextField` property:

| Event | Occurs when |
|---|---|
| [TextFieldCreated](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textfieldcreated-event/llms-full.txt), [TextFieldDeleted](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textfielddeleted-event/llms-full.txt) | A field is inserted or deleted, also by pasting, undo and redo |
| [TextFieldChanged](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textfieldchanged-event/llms-full.txt) | The text of a field has changed; formatting changes don't count |
| [TextFieldClicked](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textfieldclicked-event/llms-full.txt), [TextFieldDoubleClicked](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textfielddoubleclicked-event/llms-full.txt) | The user clicks or double-clicks a field |
| [TextFieldEntered](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textfieldentered-event/llms-full.txt), [TextFieldLeft](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/textfieldleft-event/llms-full.txt) | The input position moves into or out of a field |

Two details of these events:

- In a `TextFieldDeleted` handler, the field is already gone from the document. Only [TextFieldEventArgs.TextField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfieldeventargs/textfield-property/llms-full.txt)`.ID` and `.Name` still return valid values, so identify the deleted field by one of them.
- `TextFieldDoubleClicked` is raised only for fields whose [DoubleClickEvent](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/doubleclickevent-property/llms-full.txt) property is `true`, the default. While it is `true`, a double-click in the field doesn't select a word as it does in normal text. Set it to `false` to get the normal word selection and no event.

When a field is copied through the clipboard, the copy has the same name and ID. A `TextFieldCreated` handler can give the copy a new name.

### Work With MS Word Fields

Fields of MS Word documents, such as `MERGEFIELD`, `DATE` or `IF`, are loaded as [ApplicationField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/applicationfield/llms-full.txt) objects when [LoadSettings.ApplicationFieldFormat](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/loadsettings/applicationfieldformat-property/llms-full.txt) is set; otherwise the collection stays empty. The following handler loads the merge and date fields of a DOCX template and lists them:

```csharp
private void loadWordFieldsButton_Click(object sender, EventArgs e)
{
    LoadSettings loadSettings = new LoadSettings()
    {
        // Load MS Word fields as ApplicationField objects
        ApplicationFieldFormat = ApplicationFieldFormat.MSWord,
        ApplicationFieldTypeNames = new string[] { "MERGEFIELD", "DATE" }
    };

    textControl1.Load("template.docx", StreamType.WordprocessingML, loadSettings);

    StringBuilder list = new StringBuilder();

    foreach (ApplicationField field in textControl1.ApplicationFields)
    {
        // For a MERGEFIELD, the first parameter is the field name
        string[] parameters = field.Parameters ?? new string[0];
        list.AppendLine(field.TypeName + " " + string.Join(" ", parameters));
    }

    MessageBox.Show(list.ToString());
}
```

- [ApplicationFieldTypeNames](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/loadsettings/applicationfieldtypenames-property/llms-full.txt) limits the import to some field types; without it, all MS Word fields are loaded.
- [TypeName](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/applicationfield/typename-property/llms-full.txt) is the field type, and [Parameters](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/applicationfield/parameters-property/llms-full.txt) holds the field name and the switches. For `MERGEFIELD Address \* Upper`, the array contains `Address`, followed by the switches.

An `ApplicationField` gives your code access to the type and the parameters of the field, and changing them changes the field in the document. When you change `TypeName`, change the parameters as well. [Text](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/text-property/llms-full.txt) sets the text the field shows. `Parameters` is a .NET array, which has a fixed size: to add or remove a switch, build a new array and assign it. The following handler shows a text in every `Address` merge field and removes its `\v` switch:

```csharp
private void editWordFieldsButton_Click(object sender, EventArgs e)
{
    foreach (ApplicationField field in textControl1.ApplicationFields)
    {
        string[] parameters = field.Parameters;

        if (field.TypeName != "MERGEFIELD" || parameters == null || parameters[0] != "Address")
        {
            continue;
        }

        // The visible text of the field
        field.Text = "Baker Street, London";

        // Arrays have a fixed size, so copy the other parameters into a new one
        List<string> newParameters = new List<string>(parameters);

        if (newParameters.Remove("\\v"))
        {
            field.Parameters = newParameters.ToArray();
        }
    }
}
```

`List<string>` needs `using System.Collections.Generic;`.

The classes of the `TXTextControl.DocumentServer.Fields` namespace, such as [MergeField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver-fields/mergefield/llms-full.txt) and [IfField](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-documentserver-fields/iffield/llms-full.txt), wrap application fields and give typed access to their settings. To turn all fields into plain text, for example before a document is sent, call [ApplicationFieldCollection.Clear](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/applicationfieldcollection/clear-method/llms-full.txt) with `true`: it removes the fields and keeps their text.

Merge fields are filled with data by the `MailMerge` class, and the **Reporting** tab of the ribbon inserts them without code. [Merge Fields](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/fields/llms-full.txt) covers both.

#### Word Form Fields and Content Controls

With `ApplicationFieldFormat.MSWord`, the legacy form fields and content controls of a Word document are loaded as application fields as well, rather than as the form fields of [Form Fields and Conditional Instructions](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/form-fields/llms-full.txt). Legacy form fields have the type names `FORMTEXT`, `FORMCHECKBOX` and `FORMDROPDOWN`, and content controls the type name `SDTBLOCK`. `ApplicationFieldFormat.MSWordTXFormFields` loads the other Word fields the same way but turns form fields into TX Text Control form fields. [Content Controls and Word Form Fields](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-processing/content-controls/llms-full.txt) compares the load settings and reads and creates these controls with the field adapter classes. The values of `ApplicationFieldFormat`, including `HighEdit` for the fields of the Heiler HighEdit component, are listed in [Loading Documents](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-processing/loading/#control-the-import-with-loadsettings).

### Insert Hyperlinks and Bookmarks

On the **Insert** tab of the ribbon, the **Hyperlinks** group has a **Hyperlink** and a **Bookmark** button. Their drop-down menus insert, edit and delete hyperlinks and bookmarks in dialog boxes, such as **Edit Bookmark** with the name of the bookmark and whether it can be deleted during editing:

![The Insert tab of TX Text Control Words with the Hyperlink and Bookmark buttons in the Hyperlinks group, and the Edit Bookmark dialog box with a list of bookmarks.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/edit-bookmark-dialog.c639e6b4.webp)

The hyperlink dialog box sets the displayed text and either an address or a bookmark of the document:

![The Edit Hyperlink dialog box with the text to display, the address, a list of bookmarks in the current document and a Delete Hyperlink button.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/hyperlink-dialog.20f921a5.webp)

In code, a hyperlink, a bookmark and a link to the bookmark are inserted at the input position:

```csharp
private void insertHyperlinkButton_Click(object sender, EventArgs e)
{
    HypertextLink link = new HypertextLink("Text Control website", "https://www.textcontrol.com/")
    {
        // The text screen readers use for the link
        DescriptiveText = "Visit the Text Control website"
    };

    textControl1.HypertextLinks.Add(link);
}

private void insertBookmarkButton_Click(object sender, EventArgs e)
{
    // A bookmark named "summary" at the input position
    textControl1.DocumentTargets.Add(new DocumentTarget("summary"));
}

private void insertLinkToBookmarkButton_Click(object sender, EventArgs e)
{
    DocumentTarget target = textControl1.DocumentTargets.GetItem("summary");

    if (target != null)
    {
        textControl1.DocumentLinks.Add(new DocumentLink("See the summary", target));
    }
}
```

- The [HypertextLink constructor](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlink/constructor/llms-full.txt) takes the visible text and the [Target](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlink/target-property/llms-full.txt), such as a URL or a file name. [DescriptiveText](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlink/descriptivetext-property/llms-full.txt), new in version 34.0, is the text that screen readers use for the link.
- A [DocumentTarget](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documenttarget/constructor/llms-full.txt) has a target name and no visible text. [DocumentTargetCollection.Add](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documenttargetcollection/add-method/llms-full.txt) inserts it at the input position, and [GetItem](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documenttargetcollection/getitem-method/llms-full.txt) finds it by its target name or ID.
- A [DocumentLink](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documentlink/constructor/llms-full.txt) takes its visible text and the target it points to. [DocumentLinkCollection.Add](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documentlinkcollection/add-method/llms-full.txt) inserts it.

[DocumentTargetMarkers](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/documenttargetmarkers-property/llms-full.txt) set to `true` shows markers at the positions of the bookmarks. Hyperlinks are saved in HTML files, in RTF and Word documents and in the TX Text Control format. Saved as HTML with `StreamType.HTMLFormat`, the link opens the website in a browser:

![A browser showing an HTML file saved by TX Text Control, with the link Text Control Web Site in blue and underlined.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/hyperlink-html-browser.f8794680.webp)

#### Format Links Inserted in Code

A link that your code inserts has the formatting of the text around it; it isn't blue or underlined unless you format it:

![A Windows Forms application with the link text Text Control Web Site in black, like the text around it, and the buttons Insert Hypertext Link and Save as HTML File.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/hyperlink-unformatted.a1651367.webp)

The following method formats a link with a `Selection` object:

```csharp
private void FormatLink(HypertextLink link)
{
    // TextField.Start is one-based, Selection positions are zero-based
    Selection linkText = new Selection(link.Start - 1, link.Length)
    {
        ForeColor = Color.Blue,
        Underline = FontUnderlineStyle.Single
    };

    textControl1.Selection = linkText;

    // Put the input position at the end, inside the link, so that text typed
    // after the link doesn't take on its formatting
    textControl1.InputPosition = new InputPosition(linkText.Start + linkText.Length,
        TextFieldPosition.InsideTextField);
}
```

`Color` needs `using System.Drawing;`. Call the method after `HypertextLinks.Add`, or for each link of `textControl1.HypertextLinks`.

- [Start](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/start-property/llms-full.txt) and [Length](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/length-property/llms-full.txt) give the position of the link's text. The [Selection constructor](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/selection/constructor/llms-full.txt) takes a start and a length, and [ForeColor](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/selection/forecolor-property/llms-full.txt) and [Underline](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/selection/underline-property/llms-full.txt) set the formatting.
- Assigning the object to [TextControl.Selection](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/selection-property/llms-full.txt) applies the formatting to the text.
- The [InputPosition](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/inputposition/constructor/llms-full.txt) constructor with [TextFieldPosition](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfieldposition/enumeration/llms-full.txt)`.InsideTextField` places the input position inside the link. Assigned to [TextControl.InputPosition](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/inputposition-property/llms-full.txt), it keeps the blue, underlined formatting away from the text the user types next.

#### Edit an Existing Link

[HypertextLinkCollection.GetItem](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlinkcollection/getitem-method/llms-full.txt) without a parameter returns the link at the input position, or `null`. A dialog box of your own can use it to decide whether it edits a link or inserts a new one:

![A custom Insert Hyperlink dialog box with the text boxes Hyperlink Text and Link and the buttons OK and Cancel.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/custom-hyperlink-dialog.f18cb4e7.webp)

```csharp
private void ApplyLink(string text, string target)
{
    HypertextLink link = textControl1.HypertextLinks.GetItem();

    if (link != null)
    {
        // The input position is in a link: change it
        link.Text = text;
        link.Target = target;
    }
    else
    {
        textControl1.HypertextLinks.Add(new HypertextLink(text, target)
        {
            DoubledInputPosition = true
        });
    }
}
```

Before the dialog box opens, fill its text boxes with the link's [Text](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textfield/text-property/llms-full.txt) and [Target](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlink/target-property/llms-full.txt), or, without a link, with the selected text from [Selection.Text](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/selection/text-property/llms-full.txt). The built-in dialog boxes of [Dialog Boxes](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/ribbon-ui/dialog-boxes/llms-full.txt) do the same without code of your own.

#### List, Jump to and Remove Bookmarks

Bookmarks have no visible text, so users find them through a list. The following methods fill a `ListBox` named `targetListBox` with the names of all bookmarks, move to the selected one and delete it:

![A Delete and Go To Document Target dialog box with the targets Chapter 10 and Chapter 11 in a list and the buttons Delete and Go To.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/document-targets-list.9f078d67.webp)

```csharp
private void FillTargetList()
{
    targetListBox.Items.Clear();

    foreach (DocumentTarget target in textControl1.DocumentTargets)
    {
        targetListBox.Items.Add(target.TargetName);
    }
}

private DocumentTarget GetSelectedTarget()
{
    // The bookmark whose name is selected in the list, or null
    return targetListBox.SelectedItem is string name
        ? textControl1.DocumentTargets.GetItem(name) : null;
}

private void goToTargetButton_Click(object sender, EventArgs e)
{
    GetSelectedTarget()?.ScrollTo();
}

private void deleteTargetButton_Click(object sender, EventArgs e)
{
    DocumentTarget target = GetSelectedTarget();

    if (target != null && textControl1.DocumentTargets.Remove(target))
    {
        FillTargetList();
    }
}
```

- [TextControl.DocumentTargets](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/documenttargets-property/llms-full.txt) is enumerable, and [TargetName](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documenttarget/targetname-property/llms-full.txt) is the name a document link refers to.
- [ScrollTo](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documenttarget/scrollto-method/llms-full.txt) moves the input position to the bookmark and scrolls it into view.
- [DocumentTargetCollection.Remove](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documenttargetcollection/remove-method/llms-full.txt) deletes the bookmark and returns `false` if it couldn't be removed.

### Handle Clicks on Links

Apart from the entries of a table of contents, which scroll to their heading on Ctrl+click, TX Text Control doesn't follow links by itself, because applications treat clicks differently. It raises an event, and your handler decides what happens:

```csharp
public Form1()
{
    InitializeComponent();
    textControl1.HypertextLinkClicked += textControl1_HypertextLinkClicked;
    textControl1.DocumentLinkClicked += textControl1_DocumentLinkClicked;
}

private void textControl1_HypertextLinkClicked(object sender, HypertextLinkEventArgs e)
{
    // Open the target of the link in the default browser
    Process.Start(new ProcessStartInfo(e.HypertextLink.Target) { UseShellExecute = true });
}

private void textControl1_DocumentLinkClicked(object sender, DocumentLinkEventArgs e)
{
    e.DocumentLink.DocumentTarget?.ScrollTo();
}
```

`Process` needs `using System.Diagnostics;`.

- [HypertextLinkClicked](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/hypertextlinkclicked-event/llms-full.txt) passes a [HypertextLinkEventArgs](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/hypertextlinkeventargs/llms-full.txt) with the clicked link. Check the target before you open it, for example that it starts with `https://`, when documents come from other people: a link in a document from an unknown source can point to any file. [Handling Untrusted Documents](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/security/untrusted-documents/llms-full.txt) covers such documents.
- [DocumentLinkClicked](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/textcontrol/documentlinkclicked-event/llms-full.txt) passes a [DocumentLinkEventArgs](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documentlinkeventargs/llms-full.txt). [DocumentLink.DocumentTarget](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol/documentlink/documenttarget-property/llms-full.txt) is the target, or `null` if it no longer exists, and `ScrollTo` moves the input position there.

A click in a link also places the input position, so users can't edit the link text with the mouse when every click follows the link. To follow links only on Ctrl+click, as word processors do, check the modifier keys in a `DocumentLinkClicked` handler that replaces the one above:

```csharp
private void textControl1_DocumentLinkClicked(object sender, DocumentLinkEventArgs e)
{
    // Follow the link only while the Ctrl key is pressed
    if (Control.ModifierKeys == Keys.Control)
    {
        e.DocumentLink.DocumentTarget?.ScrollTo();
    }
}
```

The links of a table of contents are document links as well; [Tables of Contents](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/tables-of-contents/#links-from-the-entries-to-the-headings) shows how to tell them apart.

#### Open Links to Files

The target of a hyperlink can be a file instead of a URL, as the third link of this document shows:

![A document with three links: one to a website, one to a bookmark on page 3 and one to another document.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-winforms/document-with-hyperlinks.1b2449ee.webp)

A file target can be an absolute path, a `file://` URI or a path relative to the current folder, and it can end with a `#` and the name of a place in the file. The following `HypertextLinkClicked` handler, which replaces the one above, tells the kinds of targets apart. It opens URLs and files that TX Text Control can't load with their default application, and opens documents in a new instance of your application:

```csharp
// The document types this application opens itself
private static readonly Dictionary<string, StreamType> documentFormats =
    new Dictionary<string, StreamType>(StringComparer.OrdinalIgnoreCase)
    {
        [".docx"] = StreamType.WordprocessingML,
        [".doc"] = StreamType.MSWord,
        [".rtf"] = StreamType.RichTextFormat,
        [".pdf"] = StreamType.AdobePDF,
        [".tx"] = StreamType.InternalUnicodeFormat
    };

private void textControl1_HypertextLinkClicked(object sender, HypertextLinkEventArgs e)
{
    string target = e.HypertextLink.Target;

    try
    {
        // Anything that isn't an absolute URI is a path relative to the current folder
        if (!Uri.TryCreate(target, UriKind.Absolute, out Uri uri))
        {
            uri = new Uri(Path.GetFullPath(target));
        }

        if (!uri.IsFile)
        {
            // A URL: the system opens it in the default browser
            Process.Start(new ProcessStartInfo(uri.AbsoluteUri) { UseShellExecute = true });
            return;
        }

        // Remove a place in the file, such as "#chapter2"
        string path = uri.LocalPath;
        int hashPosition = path.IndexOf('#');

        if (hashPosition != -1)
        {
            path = path.Substring(0, hashPosition);
        }

        if (!File.Exists(path))
        {
            MessageBox.Show("The linked file doesn't exist: " + path);
        }
        else if (documentFormats.ContainsKey(Path.GetExtension(path)))
        {
            // A document: open it in a new instance of this application
            Process.Start(new ProcessStartInfo(Application.ExecutablePath, "\"" + path + "\""));
        }
        else
        {
            // Another file: open it with the application the system associates with it
            Process.Start(new ProcessStartInfo(path) { UseShellExecute = true });
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("The link can't be opened: " + ex.Message);
    }
}

private void Form1_Load(object sender, EventArgs e)
{
    // A new instance receives the document as its first command-line argument
    string[] args = Environment.GetCommandLineArgs();

    if (args.Length > 1 && File.Exists(args[1])
        && documentFormats.TryGetValue(Path.GetExtension(args[1]), out StreamType streamType))
    {
        textControl1.Load(args[1], streamType);
    }
}
```

Connect `Form1_Load` to the `Load` event of the form in the constructor, with `Load += Form1_Load;`, the same way as the link events. The code needs `using System.Collections.Generic;`, `using System.Diagnostics;` and `using System.IO;`.

- `Uri.TryCreate` with `UriKind.Absolute` recognizes URLs and absolute paths. `Path.GetFullPath` turns a relative path into an absolute one, and `Uri.IsFile` tells files from URLs.
- `File.Exists` checks the file before anything is started.
- `Application.ExecutablePath` is the program file of the running application. The new instance gets the quoted path as its command-line argument, and its `Load` handler reads it with `Environment.GetCommandLineArgs`, whose first element is the program itself.
- `UseShellExecute = true` hands URLs and other files to Windows, which opens them with their default application.

The setup installs the TX Text Control Words sample *Open Hyperlink* in *%USERPROFILE%\\Documents\\TX Text Control 34.0.NET for Windows Forms\\CSharp\\TX Words\\Open Hyperlink*. It opens URLs in the browser, scrolls to bookmarks and opens linked documents in a new instance of the sample, with the same steps.

### Next Steps

- [Form Fields and Conditional Instructions](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/form-fields/llms-full.txt): fields that users fill out.
- [Subtext Parts](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/subtext-parts/llms-full.txt): mark longer parts of the text that can contain fields.
- [Merge Fields](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/mail-merge/fields/llms-full.txt): fill merge fields with data.
- [Fields and Subtext Parts](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-processing/fields/llms-full.txt) in Document Processing: fields in `ServerTextControl` documents.

---

## Nearby

- **Previous:** [Tables of Contents](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/tables-of-contents/llms-full.txt)
- **Next:** [Subtext Parts](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/subtext-parts/llms-full.txt)
