UI 自動化存取內嵌物件
注意
本文件適用對象為 .NET Framework 開發人員,其想要使用 System.Windows.Automation 命名空間中定義的受控 UI 自動化類別。 如需 UI 自動化的最新資訊,請參閱 Windows 自動化 API:UI 自動化。
本主題說明如何使用 Microsoft UI 自動化,以公開文字控制項內容中的內嵌物件。
注意
內嵌物件可以包含影像、超連結、按鈕、表格或 ActiveX 控制項。
內嵌物件會被視為 UI 自動化文字提供者的子系。 這可讓這些物件透過與所有其他使用者介面 (UI) 元素相同的 UI 自動化樹狀結構來公開。 功能則是透過內嵌物件控制項類型所要求的控制項模式公開 (例如,因為超連結是文字,所以支援 TextPattern)。
包含文字內容 (「您知道嗎?」…) 和兩個內嵌物件 (鯨魚的圖片和文字超連結) 的範例文件,用作程式碼範例的目標。
範例 1
下列程式碼範例說明,如何從 UI 自動化文字提供者擷取內嵌物件的集合。 簡介中所提供的範例文件,會傳回兩個物件 (影像項目及文字項目)。
注意
影像項目應該要與一些描述影像的內建文字關聯,通常是在 NameProperty 中 (例如「藍鯨」)。 但是,當取得環繞影像物件的文字範圍時,文字資料流中不會傳回影像及描述文字。
///--------------------------------------------------------------------
/// <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>
/// Gets the children of the target selection.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="e">Event arguments.</param>
///--------------------------------------------------------------------
private void GetChildren_Click(object sender, RoutedEventArgs e)
{
// Obtain an array of child elements.
AutomationElement[] textProviderChildren;
try
{
textProviderChildren = searchRange.GetChildren();
}
catch (ElementNotAvailableException)
{
// TODO: error handling.
return;
}
// Assemble the information about the enclosing element.
StringBuilder childInformation = new StringBuilder();
childInformation.Append(textProviderChildren.Length)
.AppendLine(" child element(s).");
// Iterate through the collection of child elements and obtain
// information of interest about each.
for (int i = 0; i < textProviderChildren.Length; i++)
{
childInformation.Append("Child").Append(i).AppendLine(":");
// Obtain the name of the child control.
childInformation.Append("\tName:\t")
.AppendLine(textProviderChildren[i].Current.Name);
// Obtain the control type.
childInformation.Append("\tControl Type:\t")
.AppendLine(textProviderChildren[i].Current.ControlType.ProgrammaticName);
// Obtain the supported control patterns.
// NOTE: For the purposes of this sample we use GetSupportedPatterns().
// However, the use of GetSupportedPatterns() is strongly discouraged
// as it calls GetCurrentPattern() internally on every known pattern.
// It is therefore much more efficient to use GetCurrentPattern() for
// the specific patterns you are interested in.
AutomationPattern[] childPatterns =
textProviderChildren[i].GetSupportedPatterns();
childInformation.AppendLine("\tSupported Control Patterns:");
if (childPatterns.Length <= 0)
{
childInformation.AppendLine("\t\t\tNone");
}
else
{
foreach (AutomationPattern pattern in childPatterns)
{
childInformation.Append("\t\t\t")
.AppendLine(pattern.ProgrammaticName);
}
}
// Obtain the child elements, if any, of the child control.
TextPatternRange childRange =
documentRange.TextPattern.RangeFromChild(textProviderChildren[i]);
AutomationElement[] childRangeChildren =
childRange.GetChildren();
childInformation.Append("\tChildren: \t").Append(childRangeChildren.Length).AppendLine();
}
// Display the information about the child controls.
targetSelectionDetails.Text = childInformation.ToString();
}
'--------------------------------------------------------------------
' Gets the children of the target selection.
' <param name="sender">The object that raised the event.</param>
' <param name="e">Event arguments.</param>
'--------------------------------------------------------------------
Private Sub GetChildren_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Obtain an array of child elements.
Dim textProviderChildren() As AutomationElement
Try
textProviderChildren = searchRange.GetChildren()
Catch
' TODO: error handling.
Return
End Try
' Assemble the information about the enclosing element.
Dim childInformation As New StringBuilder()
childInformation.Append(textProviderChildren.Length).AppendLine(" child element(s).")
' Iterate through the collection of child elements and obtain
' information of interest about each.
Dim i As Integer
For i = 0 To textProviderChildren.Length - 1
childInformation.Append("Child").Append(i).AppendLine(":")
' Obtain the name of the child control.
childInformation.Append(vbTab + "Name:" + vbTab) _
.AppendLine(textProviderChildren(i).Current.Name)
' Obtain the control type.
childInformation.Append(vbTab + "Control Type:" + vbTab) _
.AppendLine(textProviderChildren(i).Current.ControlType.ProgrammaticName)
' Obtain the supported control patterns.
' NOTE: For the purposes of this sample we use GetSupportedPatterns().
' However, the use of GetSupportedPatterns() is strongly discouraged
' as it calls GetCurrentPattern() internally on every known pattern.
' It is therefore much more efficient to use GetCurrentPattern() for
' the specific patterns you are interested in.
Dim childPatterns As AutomationPattern() = _
textProviderChildren(i).GetSupportedPatterns()
childInformation.AppendLine(vbTab + "Supported Control Patterns:")
If childPatterns.Length <= 0 Then
childInformation.AppendLine(vbTab + vbTab + vbTab + "None")
Else
Dim pattern As AutomationPattern
For Each pattern In childPatterns
childInformation.Append(vbTab + vbTab + vbTab) _
.AppendLine(pattern.ProgrammaticName)
Next pattern
End If
' Obtain the child elements, if any, of the child control.
Dim childRange As TextPatternRange = _
documentRange.TextPattern.RangeFromChild(textProviderChildren(i))
Dim childRangeChildren As AutomationElement() = _
childRange.GetChildren()
childInformation.Append(vbTab + "Children: " + vbTab) _
.Append(childRangeChildren.Length).AppendLine()
Next i
' Display the information about the child controls.
targetSelectionDetails.Text = childInformation.ToString()
End Sub
範例 2
下列程式碼範例說明,如何從 UI 自動化文字提供者的內嵌物件中取得文字範圍。 擷取的文字範圍是空白範圍,起始結束點前面接的是 "… ocean.(空格)",而結尾結束點的後面則是結尾點 ".",代表內嵌超連結 (如簡介中提供的影像所示)。 即使這是空白範圍,但並不會被視為變質範圍,因為其擁有非零值的範圍。
注意
TextPattern 可以擷取文字式的內嵌物件,例如超連結,但是必須取得內嵌物件中的次要 TextPattern ,才能公開其完整的功能。
/// -------------------------------------------------------------------
/// <summary>
/// Obtains a text range spanning an embedded child
/// of a document control and displays the content of the range.
/// </summary>
/// <param name="targetTextElement">
/// The AutomationElement that represents a text control.
/// </param>
/// -------------------------------------------------------------------
private void GetRangeFromChild(AutomationElement targetTextElement)
{
TextPattern textPattern =
targetTextElement.GetCurrentPattern(TextPattern.Pattern)
as TextPattern;
if (textPattern == null)
{
// Target control doesn't support TextPattern.
return;
}
// Obtain a text range spanning the entire document.
TextPatternRange textRange = textPattern.DocumentRange;
// Retrieve the embedded objects within the range.
AutomationElement[] embeddedObjects = textRange.GetChildren();
// Retrieve and display text value of embedded object.
foreach (AutomationElement embeddedObject in embeddedObjects)
{
if ((bool)embeddedObject.GetCurrentPropertyValue(
AutomationElement.IsTextPatternAvailableProperty))
{
// For full functionality a secondary TextPattern should
// be obtained from the embedded object.
// embeddedObject must be a child of the text provider.
TextPatternRange embeddedObjectRange =
textPattern.RangeFromChild(embeddedObject);
// GetText(-1) retrieves all text in the range.
// Typically a more limited amount of text would be
// retrieved for performance and security reasons.
Console.WriteLine(embeddedObjectRange.GetText(-1));
}
}
}
''' -------------------------------------------------------------------
''' <summary>
''' Obtains a text range spanning an embedded child
''' of a document control and displays the content of the range.
''' </summary>
''' <param name="targetTextElement">
''' The AutomationElement. representing a text control.
''' </param>
''' -------------------------------------------------------------------
Private Sub GetRangeFromChild( _
ByVal targetTextElement As AutomationElement)
Dim textPattern As TextPattern = _
DirectCast( _
targetTextElement.GetCurrentPattern(textPattern.Pattern), _
TextPattern)
If (textPattern Is Nothing) Then
' Target control doesn't support TextPattern.
Return
End If
' Obtain a text range spanning the entire document.
Dim textRange As TextPatternRange = textPattern.DocumentRange
' Retrieve the embedded objects within the range.
Dim embeddedObjects() As AutomationElement = textRange.GetChildren()
Dim embeddedObject As AutomationElement
For Each embeddedObject In embeddedObjects
If (embeddedObject.GetCurrentPropertyValue( _
AutomationElement.IsTextPatternAvailableProperty) = True) Then
' For full functionality a secondary TextPattern should
' be obtained from the embedded object.
' embeddedObject must be a child of the text provider.
Dim embeddedObjectRange As TextPatternRange = _
textPattern.RangeFromChild(embeddedObject)
' GetText(-1) retrieves all text in the range.
' Typically a more limited amount of text would be
' retrieved for performance and security reasons.
Console.WriteLine(embeddedObjectRange.GetText(-1))
End If
Next
End Sub