利用 UI 自動化尋找和反白顯示文字
注意
本文件適用對象為 .NET Framework 開發人員,其想要使用 System.Windows.Automation 命名空間中定義的受控 UI 自動化類別。 如需 UI 自動化的最新資訊,請參閱 Windows 自動化 API:UI 自動化。
本主題示範如何使用 Microsoft UI 自動化,循序搜尋並反白顯示文字控制項內容中出現的每個字串。
範例
下列範例會從文字控制項取得 TextPattern 物件。 然後會使用此 TextPattern 的 DocumentRange 屬性來建立代表整個文件文字內容的 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