使用 UI 自动化访问嵌入式对象
备注
本文档适用于想要使用 System.Windows.Automation 命名空间中定义的托管 UI 自动化类的 .NET Framework 开发人员。 有关 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