使用 UI 自动化查找和突出显示文本

备注

本文档适用于想要使用 System.Windows.Automation 命名空间中定义的托管 UI 自动化类的 .NET Framework 开发人员。 有关 UI 自动化的最新信息,请参阅 Windows 自动化 API:UI 自动化

本主题演示如何使用 Microsoft UI 自动化按顺序搜索并突出显示文本控件内容中每个字符串的匹配项。

示例

以下示例从文本控件中获取 TextPattern 对象, 接着使用此 TextPatternDocumentRange 属性创建表示整个文档文本内容的 TextPatternRange 对象, 然后,为顺序搜索和突出显示功能创建另外两个 TextPatternRange 对象。

///--------------------------------------------------------------------
/// <summary>
/// Starts the target application.
/// </summary>
/// <param name="app">
/// The application to start.
/// </param>
/// <returns>The automation element for the app main window.</returns>
/// <remarks>
/// Three WPF documents, a rich text document, and a plain text document
/// are provided in the Content folder of the TextProvider project.
/// </remarks>
///--------------------------------------------------------------------
private AutomationElement StartApp(string app)
{
    // Start application.
    Process p = Process.Start(app);

    // Give the target application some time to start.
    // For Win32 applications, WaitForInputIdle can be used instead.
    // Another alternative is to listen for WindowOpened events.
    // Otherwise, an ArgumentException results when you try to
    // retrieve an automation element from the window handle.
    Thread.Sleep(2000);

    targetResult.Content =
        WPFTarget +
        " started. \n\nPlease load a document into the target " +
        "application and click the 'Find edit control' button above. " +
        "\n\nNOTE: Documents can be found in the 'Content' folder of the FindText project.";
    targetResult.Background = Brushes.LightGreen;

    // Return the automation element for the app main window.
    return (AutomationElement.FromHandle(p.MainWindowHandle));
}
'--------------------------------------------------------------------
' Starts the target application.
' <param name="app">
' The application to start.
' <returns>The automation element for the app main window.</returns>
' Three WPF documents, a rich text document, and a plain text document
' are provided in the Content folder of the TextProvider project.
'--------------------------------------------------------------------
Private Function StartApp(ByVal app As String) As AutomationElement
    ' Start application.
    Dim p As Process = Process.Start(app)

    ' Give the target application some time to start.
    ' For Win32 applications, WaitForInputIdle can be used instead.
    ' Another alternative is to listen for WindowOpened events.
    ' Otherwise, an ArgumentException results when you try to
    ' retrieve an automation element from the window handle.
    Thread.Sleep(2000)

    targetResult.Content = WPFTarget + " started. " + vbLf + vbLf + _
    "Please load a document into the target application and click " + _
    "the 'Find edit control' button above. " + vbLf + vbLf + _
    "NOTE: Documents can be found in the 'Content' folder of the FindText project."
    targetResult.Background = Brushes.LightGreen

    ' Return the automation element for the app main window.
    Return AutomationElement.FromHandle(p.MainWindowHandle)

End Function 'StartApp
///--------------------------------------------------------------------
/// <summary>
/// Finds the text control in our target.
/// </summary>
/// <param name="src">The object that raised the event.</param>
/// <param name="e">Event arguments.</param>
/// <remarks>
/// Initializes the TextPattern object and event handlers.
/// </remarks>
///--------------------------------------------------------------------
private void FindTextProvider_Click(object src, RoutedEventArgs e)
{
    // Set up the conditions for finding the text control.
    PropertyCondition documentControl = new PropertyCondition(
        AutomationElement.ControlTypeProperty,
        ControlType.Document);
    PropertyCondition textPatternAvailable = new PropertyCondition(
        AutomationElement.IsTextPatternAvailableProperty, true);
    AndCondition findControl =
        new AndCondition(documentControl, textPatternAvailable);

    // Get the Automation Element for the first text control found.
    // For the purposes of this sample it is sufficient to find the
    // first text control. In other cases there may be multiple text
    // controls to sort through.
    targetDocument =
        targetWindow.FindFirst(TreeScope.Descendants, findControl);

    // Didn't find a text control.
    if (targetDocument == null)
    {
        targetResult.Content =
            WPFTarget +
            " does not contain a Document control type.";
        targetResult.Background = Brushes.Salmon;
        startWPFTargetButton.IsEnabled = false;
        return;
    }

    // Get required control patterns
    targetTextPattern =
        targetDocument.GetCurrentPattern(
        TextPattern.Pattern) as TextPattern;

    // Didn't find a text control that supports TextPattern.
    if (targetTextPattern == null)
    {
        targetResult.Content =
            WPFTarget +
            " does not contain an element that supports TextPattern.";
        targetResult.Background = Brushes.Salmon;
        startWPFTargetButton.IsEnabled = false;
        return;
    }

    // Text control is available so display the client controls.
    infoGrid.Visibility = Visibility.Visible;

    targetResult.Content =
        "Text provider found.";
    targetResult.Background = Brushes.LightGreen;

    // Initialize the document range for the text of the document.
    documentRange = targetTextPattern.DocumentRange;

    // Initialize the client's search buttons.
    if (targetTextPattern.DocumentRange.GetText(1).Length > 0)
    {
        searchForwardButton.IsEnabled = true;
    }
    // Initialize the client's search TextBox.
    searchString.IsEnabled = true;

    // Check if the text control supports text selection
    if (targetTextPattern.SupportedTextSelection ==
        SupportedTextSelection.None)
    {
        targetResult.Content = "Unable to select text.";
        targetResult.Background = Brushes.Salmon;
        return;
    }

    // Edit control found so remove the find button from the client.
    findEditButton.Visibility = Visibility.Collapsed;

    // Initialize the client with the current target selection, if any.
    NotifySelectionChanged();

    // Search starts at beginning of doc and goes forward
    searchBackward = false;

    // Initialize a text changed listener.
    // An instance of TextPatternRange will become invalid if
    // one of the following occurs:
    // 1) The text in the provider changes via some user activity.
    // 2) ValuePattern.SetValue is used to programmatically change
    // the value of the text in the provider.
    // The only way the client application can detect if the text
    // has changed (to ensure that the ranges are still valid),
    // is by setting a listener for the TextChanged event of
    // the TextPattern. If this event is raised, the client needs
    // to update the targetDocumentRange member data to ensure the
    // user is working with the updated text.
    // Clients must always anticipate the possibility that the text
    // can change underneath them.
    Automation.AddAutomationEventHandler(
        TextPattern.TextChangedEvent,
        targetDocument,
        TreeScope.Element,
        TextChanged);

    // Initialize a selection changed listener.
    // The target selection is reflected in the client.
    Automation.AddAutomationEventHandler(
        TextPattern.TextSelectionChangedEvent,
        targetDocument,
        TreeScope.Element,
        OnTextSelectionChange);
}
'--------------------------------------------------------------------
' Finds the text control in our target.
' <param name="src">The object that raised the event.</param>
' <param name="e">Event arguments.</param>
' Initializes the TextPattern object and event handlers.
'--------------------------------------------------------------------
Private Sub FindTextProvider_Click( _
ByVal src As Object, ByVal e As RoutedEventArgs)
    ' Set up the conditions for finding the text control.
    Dim documentControl As New PropertyCondition( _
    AutomationElement.ControlTypeProperty, ControlType.Document)
    Dim textPatternAvailable As New PropertyCondition( _
    AutomationElement.IsTextPatternAvailableProperty, True)
    Dim findControl As New AndCondition(documentControl, textPatternAvailable)

    ' Get the Automation Element for the first text control found.
    ' For the purposes of this sample it is sufficient to find the
    ' first text control. In other cases there may be multiple text
    ' controls to sort through.
    targetDocument = targetWindow.FindFirst(TreeScope.Descendants, findControl)

    ' Didn't find a text control.
    If targetDocument Is Nothing Then
        targetResult.Content = _
        WPFTarget + " does not contain a Document control type."
        targetResult.Background = Brushes.Salmon
        startWPFTargetButton.IsEnabled = False
        Return
    End If

    ' Get required control patterns
    targetTextPattern = DirectCast( _
    targetDocument.GetCurrentPattern(TextPattern.Pattern), TextPattern)

    ' Didn't find a text control that supports TextPattern.
    If targetTextPattern Is Nothing Then
        targetResult.Content = WPFTarget + _
        " does not contain an element that supports TextPattern."
        targetResult.Background = Brushes.Salmon
        startWPFTargetButton.IsEnabled = False
        Return
    End If
    ' Text control is available so display the client controls.
    infoGrid.Visibility = Visibility.Visible

    targetResult.Content = "Text provider found."
    targetResult.Background = Brushes.LightGreen

    ' Initialize the document range for the text of the document.
    documentRange = targetTextPattern.DocumentRange

    ' Initialize the client's search buttons.
    If targetTextPattern.DocumentRange.GetText(1).Length > 0 Then
        searchForwardButton.IsEnabled = True
    End If
    ' Initialize the client's search TextBox.
    searchString.IsEnabled = True

    ' Check if the text control supports text selection
    If targetTextPattern.SupportedTextSelection = SupportedTextSelection.None Then
        targetResult.Content = "Unable to select text."
        targetResult.Background = Brushes.Salmon
        Return
    End If

    ' Edit control found so remove the find button from the client.
    findEditButton.Visibility = Visibility.Collapsed

    ' Initialize the client with the current target selection, if any.
    NotifySelectionChanged()

    ' Search starts at beginning of doc and goes forward
    searchBackward = False

    ' Initialize a text changed listener.
    ' An instance of TextPatternRange will become invalid if
    ' one of the following occurs:
    ' 1) The text in the provider changes via some user activity.
    ' 2) ValuePattern.SetValue is used to programmatically change
    ' the value of the text in the provider.
    ' The only way the client application can detect if the text
    ' has changed (to ensure that the ranges are still valid),
    ' is by setting a listener for the TextChanged event of
    ' the TextPattern. If this event is raised, the client needs
    ' to update the targetDocumentRange member data to ensure the
    ' user is working with the updated text.
    ' Clients must always anticipate the possibility that the text
    ' can change underneath them.
    Dim onTextChanged As AutomationEventHandler = _
    New AutomationEventHandler(AddressOf TextChanged)
    Automation.AddAutomationEventHandler( _
    TextPattern.TextChangedEvent, targetDocument, TreeScope.Element, onTextChanged)
    ' Initialize a selection changed listener.
    ' The target selection is reflected in the client.
    Dim onSelectionChanged As AutomationEventHandler = _
    New AutomationEventHandler(AddressOf OnTextSelectionChange)
    Automation.AddAutomationEventHandler( _
    TextPattern.TextSelectionChangedEvent, targetDocument, _
    TreeScope.Element, onSelectionChanged)

End Sub
///--------------------------------------------------------------------
/// <summary>
/// Handles changes to the search text in the client.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="e">Event arguments.</param>
/// <remarks>
/// Reset all controls if user changes search text
/// </remarks>
///--------------------------------------------------------------------
void SearchString_Change(object sender, TextChangedEventArgs e)
{
    int startPoints = documentRange.CompareEndpoints(
        TextPatternRangeEndpoint.Start,
        searchRange,
        TextPatternRangeEndpoint.Start);
    int endPoints = documentRange.CompareEndpoints(
        TextPatternRangeEndpoint.End,
        searchRange,
        TextPatternRangeEndpoint.End);

    // If the starting endpoints of the document range and the search
    // range are equivalent then we can search forward only since the
    // search range is at the start of the document.
    if (startPoints == 0)
    {
        searchForwardButton.IsEnabled = true;
        searchBackwardButton.IsEnabled = false;
    }
    // If the ending endpoints of the document range and the search
    // range are identical then we can search backward only since the
    // search range is at the end of the document.
    else if (endPoints == 0)
    {
        searchForwardButton.IsEnabled = false;
        searchBackwardButton.IsEnabled = true;
    }
    // Otherwise we can search both directions.
    else
    {
        searchForwardButton.IsEnabled = true;
        searchBackwardButton.IsEnabled = true;
    }
}

///--------------------------------------------------------------------
/// <summary>
/// Handles the Search button click.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="e">Event arguments.</param>
/// <remarks>Find the text specified in the text box.</remarks>
///--------------------------------------------------------------------
void SearchDirection_Click(object sender, RoutedEventArgs e)
{
    Button searchDirection = (Button)sender;

    // Are we searching backward through the text control?
    searchBackward =
        ((traversalDirection)searchDirection.Tag == traversalDirection.Backward);

    // Check if search text entered
    if (searchString.Text.Trim() == "")
    {
        targetResult.Content = "No search criteria.";
        targetResult.Background = Brushes.Salmon;
        return;
    }

    // Does target range support text selection?
    if (targetTextPattern.SupportedTextSelection ==
        SupportedTextSelection.None)
    {
        targetResult.Content = "Unable to select text.";
        targetResult.Background = Brushes.Salmon;
        return;
    }
    // Does target range support multiple selections?
    if (targetTextPattern.SupportedTextSelection ==
        SupportedTextSelection.Multiple)
    {
        targetResult.Content = "Multiple selections present.";
        targetResult.Background = Brushes.Salmon;
        return;
    }

    // Clone the document range since we modify the endpoints
    // as we search.
    TextPatternRange documentRangeClone = documentRange.Clone();

    // Move the cloned document range endpoints to enable the
    // selection of the next matching text range.
    TextPatternRange[] selectionRange =
        targetTextPattern.GetSelection();
    if (selectionRange[0] != null)
    {
        if (searchBackward)
        {
            documentRangeClone.MoveEndpointByRange(
                TextPatternRangeEndpoint.End,
                selectionRange[0],
                TextPatternRangeEndpoint.Start);
        }
        else
        {
            documentRangeClone.MoveEndpointByRange(
                TextPatternRangeEndpoint.Start,
                selectionRange[0],
                TextPatternRangeEndpoint.End);
        }
    }

    // Find the text specified in the Search textbox.
    // Clone the search range since we need to modify it.
    TextPatternRange searchRangeClone = searchRange.Clone();
    // backward = false? -- search forward, otherwise backward.
    // ignoreCase = false? -- search is case sensitive.
    searchRange =
        documentRangeClone.FindText(
        searchString.Text, searchBackward, false);

    // Search unsuccessful.
    if (searchRange == null)
    {
        // Search string not found at all.
        if (documentRangeClone.CompareEndpoints(
            TextPatternRangeEndpoint.Start,
            searchRangeClone,
            TextPatternRangeEndpoint.Start) == 0)
        {
            targetResult.Content = "Text not found.";
            targetResult.Background = Brushes.Wheat;
            searchBackwardButton.IsEnabled = false;
            searchForwardButton.IsEnabled = false;
        }
        // End of document (either the start or end of the document
        // range depending on search direction) was reached before
        // finding another occurrence of the search string.
        else
        {
            targetResult.Content = "End of document reached.";
            targetResult.Background = Brushes.Wheat;
            if (!searchBackward)
            {
                searchRangeClone.MoveEndpointByRange(
                    TextPatternRangeEndpoint.Start,
                    documentRange,
                    TextPatternRangeEndpoint.End);
                searchBackwardButton.IsEnabled = true;
                searchForwardButton.IsEnabled = false;
            }
            else
            {
                searchRangeClone.MoveEndpointByRange(
                    TextPatternRangeEndpoint.End,
                    documentRange,
                    TextPatternRangeEndpoint.Start);
                searchBackwardButton.IsEnabled = false;
                searchForwardButton.IsEnabled = true;
            }
        }
        searchRange = searchRangeClone;
    }
    // The search string was found.
    else
    {
        targetResult.Content = "Text found.";
        targetResult.Background = Brushes.LightGreen;
    }

    searchRange.Select();
    // Scroll the selection into view and align with top of viewport
    searchRange.ScrollIntoView(true);
    // The WPF target doesn't show selected text as highlighted unless
    // the window has focus.
    targetWindow.SetFocus();
}
'--------------------------------------------------------------------
' Handles changes to the search text in the client.
' <param name="sender">The object that raised the event.</param>
' <param name="e">Event arguments.</param>
' Reset all controls if user changes search text
'--------------------------------------------------------------------
Private Sub SearchString_Change( _
ByVal sender As Object, ByVal e As TextChangedEventArgs)
    Dim startPoints As Integer = _
    documentRange.CompareEndpoints( _
    TextPatternRangeEndpoint.Start, searchRange, _
    TextPatternRangeEndpoint.Start)
    Dim endPoints As Integer = _
    documentRange.CompareEndpoints(TextPatternRangeEndpoint.End, _
    searchRange, TextPatternRangeEndpoint.End)

    ' If the starting endpoints of the document range and the search
    ' range are equivalent then we can search forward only since the
    ' search range is at the start of the document.
    If startPoints = 0 Then
        searchForwardButton.IsEnabled = True
        searchBackwardButton.IsEnabled = False
        ' If the ending endpoints of the document range and the search
        ' range are identical then we can search backward only since the
        ' search range is at the end of the document.
    ElseIf endPoints = 0 Then
        searchForwardButton.IsEnabled = False
        searchBackwardButton.IsEnabled = True
        ' Otherwise we can search both directions.
    Else
        searchForwardButton.IsEnabled = True
        searchBackwardButton.IsEnabled = True
    End If

End Sub

'--------------------------------------------------------------------
' Handles the Search button click.
' <param name="sender">The object that raised the event.</param>
' <param name="e">Event arguments.</param>
' <remarks>Find the text specified in the text box.</remarks>
'--------------------------------------------------------------------
Private Sub SearchDirection_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim searchDirection As Button = CType(sender, Button)

    ' Are we searching backward through the text control?
    searchBackward = _
    (CType(searchDirection.Tag, traversalDirection) = traversalDirection.Backward)

    ' Check if search text entered
    If searchString.Text.Trim() = "" Then
        targetResult.Content = "No search criteria."
        targetResult.Background = Brushes.Salmon
        Return
    End If

    ' Does target range support text selection?
    If targetTextPattern.SupportedTextSelection = SupportedTextSelection.None Then
        targetResult.Content = "Unable to select text."
        targetResult.Background = Brushes.Salmon
        Return
    End If
    ' Does target range support multiple selections?
    If targetTextPattern.SupportedTextSelection = SupportedTextSelection.Multiple Then
        targetResult.Content = "Multiple selections present."
        targetResult.Background = Brushes.Salmon
        Return
    End If
    ' Clone the document range since we modify the endpoints
    ' as we search.
    Dim documentRangeClone As TextPatternRange = documentRange.Clone()

    ' Move the cloned document range endpoints to enable the
    ' selection of the next matching text range.
    Dim selectionRange As TextPatternRange() = targetTextPattern.GetSelection()
    If Not (selectionRange(0) Is Nothing) Then
        If searchBackward Then
            documentRangeClone.MoveEndpointByRange( _
            TextPatternRangeEndpoint.End, selectionRange(0), _
            TextPatternRangeEndpoint.Start)
        Else
            documentRangeClone.MoveEndpointByRange( _
            TextPatternRangeEndpoint.Start, selectionRange(0), _
            TextPatternRangeEndpoint.End)
        End If
    End If

    ' Find the text specified in the Search textbox.
    ' Clone the search range since we need to modify it.
    Dim searchRangeClone As TextPatternRange = searchRange.Clone()
    ' backward = false? -- search forward, otherwise backward.
    ' ignoreCase = false? -- search is case sensitive.
    searchRange = documentRangeClone.FindText(searchString.Text, searchBackward, False)

    ' Search unsuccessful.
    If searchRange Is Nothing Then
        ' Search string not found at all.
        If documentRangeClone.CompareEndpoints( _
        TextPatternRangeEndpoint.Start, searchRangeClone, _
        TextPatternRangeEndpoint.Start) = 0 Then
            targetResult.Content = "Text not found."
            targetResult.Background = Brushes.Wheat
            searchBackwardButton.IsEnabled = False
            searchForwardButton.IsEnabled = False
        Else
            ' End of document (either the start or end of the document
            ' range depending on search direction) was reached before
            ' finding another occurrence of the search string.
            targetResult.Content = "End of document reached."
            targetResult.Background = Brushes.Wheat
            If Not searchBackward Then
                searchRangeClone.MoveEndpointByRange( _
                TextPatternRangeEndpoint.Start, documentRange, _
                TextPatternRangeEndpoint.End)
                searchBackwardButton.IsEnabled = True
                searchForwardButton.IsEnabled = False
            Else
                searchRangeClone.MoveEndpointByRange( _
                TextPatternRangeEndpoint.End, documentRange, _
                TextPatternRangeEndpoint.Start)
                searchBackwardButton.IsEnabled = False
                searchForwardButton.IsEnabled = True
            End If
        End If
        searchRange = searchRangeClone
    Else
        ' The search string was found.
        targetResult.Content = "Text found."
        targetResult.Background = Brushes.LightGreen
    End If

    searchRange.Select()
    ' Scroll the selection into view and align with top of viewport
    searchRange.ScrollIntoView(True)
    ' The WPF target doesn't show selected text as highlighted unless
    ' the window has focus.
    targetWindow.SetFocus()

End Sub

请参阅