# Accessibility and UI Automation

> Let UI Automation clients read and change a Windows Forms TextControl: TextPattern attributes, ValuePattern, EM_REPLACESEL, AccessibleName and key tips.

- **Product:** TX Text Control .NET for Windows Forms
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for Windows Forms > Integration Guide > Accessibility and UI Automation
- **3578 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/integration/accessibility/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/integration/accessibility/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-winforms/integration/accessibility/llms-full.txt

---

Screen readers, speech recognition software and automated UI tests don't see your application the way a user does: they ask Windows for its elements through Microsoft UI Automation. The `TextControl` answers these requests with the text of its document and the formatting of each part, and it can be named, reached by keyboard and changed from outside. Here is what TX Text Control .NET for Windows Forms exposes, how an accessibility tool or a test finds and reads the editor, and what your application adds on its side.

### What the TextControl Exposes

Since version 27.0, the `TextControl` implements two control patterns of UI Automation, in Windows Forms and in WPF:

- **TextPattern** gives access to the text of the document in ranges, and each range reports its format and style attributes, such as the font, the size, bold and italic, the alignment and the indents.
- **ValuePattern** represents the complete text as one string, which a client can read and replace.

A UI Automation client recognizes the `TextControl` by these properties of its automation element:

| Property | Value |
|---|---|
| `LocalizedControlType` | `"document"` |
| `ClassName` | `"TXTextControl.TextControl"` |
| `FrameworkId` | `"WinForm"` |

The TextPattern of the `TextControl` supports `DocumentRange`, `GetSelection`, `GetVisibleRanges` and `RangeFromPoint` for getting text ranges, and raises `TextSelectionChangedEvent` when the input position changes. On a range, it supports `GetText`, `Select`, `GetBoundingRectangles`, `GetEnclosingElement`, `ScrollIntoView`, `Clone`, `Compare`, `CompareEndpoints`, `ExpandToEnclosingUnit`, `Move`, `MoveEndpointByUnit`, `MoveEndpointByRange` and `GetAttributeValue`.

### Text Attributes a Client Can Read

`GetAttributeValue` of a text range returns these attributes of the text in the `TextControl`:

| Attributes | TextPattern fields |
|---|---|
| Colors | `BackgroundColorAttribute`, `ForegroundColorAttribute` |
| Font | `FontNameAttribute`, `FontSizeAttribute`, `FontWeightAttribute`, `IsItalicAttribute`, `IsSubscriptAttribute`, `IsSuperscriptAttribute`, `StrikethroughStyleAttribute`, `UnderlineStyleAttribute` |
| Paragraph | `HorizontalTextAlignmentAttribute`, `IndentationFirstLineAttribute`, `IndentationLeadingAttribute`, `IndentationTrailingAttribute`, `TabsAttribute`, `TextFlowDirectionsAttribute` |
| Page | `MarginBottomAttribute`, `MarginLeadingAttribute`, `MarginTopAttribute`, `MarginTrailingAttribute` |
| Other | `CultureAttribute`, `IsReadOnlyAttribute` |

`StyleNameAttribute` is not supported. For a range whose characters differ in an attribute, such as partly bold text, the value is `TextPattern.MixedAttributeValue`.

### Read the Editor From Another Application

The following console application reads the `TextControl` of a running application named *MyEditor*: it prints the text of the document, reports whether the selected text is bold and writes a line whenever the user moves the input position. It uses the UI Automation client of .NET, in the namespace `System.Windows.Automation`; in a .NET project, set `<UseWPF>true</UseWPF>` and a target framework such as `net8.0-windows` in the project file to reference it.

```csharp
using System;
using System.Diagnostics;
using System.Windows.Automation;
using System.Windows.Automation.Text;

// Find the main window of the running editor application
Process editor = Process.GetProcessesByName("MyEditor")[0];
AutomationElement window = AutomationElement.FromHandle(editor.MainWindowHandle);

// Find the first element that exposes its text through the TextPattern
AutomationElement textControl = window.FindFirst(TreeScope.Descendants,
    new PropertyCondition(AutomationElement.IsTextPatternAvailableProperty, true));

// Identify the element as a TextControl
string className = textControl.Current.ClassName;
Console.WriteLine($"{className}, {textControl.Current.FrameworkId}, {textControl.Current.LocalizedControlType}");

if (className == "TXTextControl.TextControl" || className == "TXTextControl.WPF.TextControl")
{
    TextPattern textPattern = (TextPattern)textControl.GetCurrentPattern(TextPattern.Pattern);

    // The complete text of the document
    Console.WriteLine(textPattern.DocumentRange.GetText(-1));

    // Is the selected text bold?
    TextPatternRange selection = textPattern.GetSelection()[0];
    object fontWeight = selection.GetAttributeValue(TextPattern.FontWeightAttribute);
    bool isBold = fontWeight != TextPattern.MixedAttributeValue && (int)fontWeight == 700;
    Console.WriteLine($"Selection is bold: {isBold}");

    // Report every move of the input position
    Automation.AddAutomationEventHandler(TextPattern.TextSelectionChangedEvent,
        textControl, TreeScope.Element,
        (sender, e) => Console.WriteLine("The selection has changed."));

    Console.ReadLine();
}
```

The code works as follows:

- **`AutomationElement.FromHandle`** gets the element of the main window, and **`FindFirst`** searches its descendants for the first element that supports the TextPattern.
- **`ClassName`** tells a `TextControl` of Windows Forms from one of WPF, so the same client works with both.
- **`DocumentRange.GetText(-1)`** returns the whole text; `-1` means no length limit.
- **`GetAttributeValue(TextPattern.FontWeightAttribute)`** returns 700 for bold text and `MixedAttributeValue` for a selection that is partly bold.
- **`TextSelectionChangedEvent`** is raised by the `TextControl` whenever the input position changes.

The same calls serve automated UI tests: a test starts the application, finds the editor this way and compares its text and attributes with the expected result.

### Change the Text From Outside

The ValuePattern replaces the complete text of the document:

```csharp
// The ValuePattern replaces the whole text of the document
if (textControl.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern))
{
    ((ValuePattern)pattern).SetValue("New text");
}
```

The ValuePattern has no method to insert text at the input position or to replace the selection. For that, the `TextControl` handles the standard edit control messages of Win32, `EM_GETSEL`, `EM_SETSEL` and `EM_REPLACESEL`, which a client sends to the window of the editor. In Windows Forms, the `TextControl` is a native window, and its handle is the `NativeWindowHandle` property of the automation element:

```csharp
IntPtr hWnd = new IntPtr(textControl.Current.NativeWindowHandle);
```

`EM_REPLACESEL` inserts the text at the input position when nothing is selected. Microsoft documents the messages and their parameters.

To send a message, the client imports two functions of *user32.dll*, `SendMessageTimeout` and `IsWindow`, and defines the numbers of the three messages:

```csharp
using System;
using System.Runtime.InteropServices;

internal static class Win32
{
    internal const int EM_GETSEL = 0x00B0;
    internal const int EM_SETSEL = 0x00B1;
    internal const int EM_REPLACESEL = 0x00C2;

    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern IntPtr SendMessageTimeout(IntPtr hwnd, int uMsg, IntPtr wParam,
        IntPtr lParam, int flags, int uTimeout, out IntPtr result);

    [DllImport("user32.dll", ExactSpelling = true)]
    internal static extern bool IsWindow(IntPtr hWnd);
}
```

With this class, the client inserts text at the input position of the `TextControl`, or replaces the selected text, with the handle `hWnd` from above:

```csharp
if (Win32.IsWindow(hWnd))
{
    // Copy the text into an unmanaged buffer
    IntPtr text = Marshal.StringToHGlobalUni("New text");

    // Wait at most 10 seconds for the editor to process the message
    IntPtr timeResult = Win32.SendMessageTimeout(hWnd, Win32.EM_REPLACESEL,
        IntPtr.Zero, text, 1, 10000, out IntPtr messageResult);

    Marshal.FreeHGlobal(text);

    bool inserted = timeResult != IntPtr.Zero && messageResult.ToInt32() == 1;
}
```

The code works as follows:

- `IsWindow` checks that the handle still belongs to a window, for example after the application of the editor has been closed.
- `Marshal.StringToHGlobalUni` copies the string into unmanaged memory. The system passes this buffer on to the process of the editor, so the client needs no memory of its own in that process. `Marshal.FreeHGlobal` releases the buffer afterward.
- `SendMessageTimeout` returns `IntPtr.Zero` when the 10,000 milliseconds passed before the text was inserted. `messageResult` is 1 when the text was inserted and 0 when it was not.

### Name the Editor for Screen Readers

The `TextControl`, the `ButtonBar`, the `RulerBar` and the `StatusBar` derive from `System.Windows.Forms.Control` and inherit its accessibility properties. Give an editor a name and a description when a form has more than one, so that a screen reader can tell them apart:

```csharp
// Name and describe the editor for screen readers
textControl1.AccessibleName = "Letter";
textControl1.AccessibleDescription = "The text of the letter to the customer";
```

`AccessibleName`, `AccessibleDescription` and `AccessibleRole` are also in the **Properties** window, in the **Accessibility** category.

### Work Without a Mouse

Users who can't use a mouse work with the keyboard alone:

- **In the document**, the `TextControl` supports the keyboard shortcuts of a word processor for moving, selecting, formatting and editing. [Keyboard Shortcuts](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/editing-text/keyboard-shortcuts/llms-full.txt) lists them.
- **In the ribbon**, every item of the predefined tabs has a key tip, the letter the user presses after **Alt** to reach it. Give buttons of your own a key tip with [KeyTip](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-windows-forms-ribbon/ribbonbutton/keytip-property/llms-full.txt), or take the one of a built-in command from [ResourceProvider.GetKeyTip](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-windows-forms/resourceprovider/getkeytip-method/llms-full.txt). Tabs and groups have a [KeyTip](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/reference/txtextcontrol-windows-forms-ribbon/ribbontab/keytip-property/llms-full.txt) property as well.

### Next Steps

- [High DPI and Display Scaling](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/integration/dpi-scaling/llms-full.txt): a sharp editor at large scale factors.
- [Keyboard Shortcuts](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/editing-text/keyboard-shortcuts/llms-full.txt): the keys of the editor.
- [Custom Tabs and Commands](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/document-editor/ribbon-ui/custom-tabs/llms-full.txt): ribbon buttons with key tips and tool tips.
- [PDF/A](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/pdf/pdf-a/llms-full.txt): archivable and accessible PDF documents created from the editor's content.

---

## Nearby

- **Previous:** [High DPI and Display Scaling](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/integration/dpi-scaling/llms-full.txt)
- **Next:** [AI Coding Assistants](https://docs.textcontrol.com/tx-text-control-dotnet-winforms/integration/coding-assistants/llms-full.txt)
