1 '*****************************************************************************
2 ' * File: InvokePatternApp.vb
5 '* This sample demonstrates the UI Automation ExpandCollapse, Invoke, and
6 '* Toggle control pattern classes.
8 '* The sample consists of a Windows Presentation Foundation (WPF) client and
9 '* a WPF target containing a variety of TreeView controls. The client uses the
10 '* ExpandCollapse, Invoke, and Toggle control patterns to interact with the
11 '* controls in the target.
13 '* The functionality demonstrated by the sample includes the ability to
14 '* report target control properties and call the supported methods exposed
15 '* by the respective control patterns such as invoke, toggle, expand or collapse.
18 '* 1. Hidden child controls are not present in the UI Automation tree until
19 '* they are created and displayed by expanding the top-level TreeViewItem.
20 '* 2. Unless a menu item performs an action other than expanding/collapsing,
21 '* it does not support the Invoke pattern (ie, the 'Help' menu item in
22 '* notepad supports ExpandCollapse but does not support Invoke, however,
23 '* the 'About Notepad' menu item under the 'Help' menu supports
24 '* InvokePattern as it creates an instance of the 'About Notepad' dialog).
27 ' * This file is part of the Microsoft .NET Framework SDK Code Samples.
29 ' * Copyright (C) Microsoft Corporation. All rights reserved.
31 ' * This source code is intended only as a supplement to Microsoft
32 ' * Development Tools and/or on-line documentation. See these other
33 ' * materials for detailed information regarding Microsoft code samples.
35 ' * THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
36 ' * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
37 ' * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
38 ' * PARTICULAR PURPOSE.
40 ' ****************************************************************************
43 Imports System
.ComponentModel
44 Imports System
.Windows
45 Imports System
.Windows
.Documents
46 Imports System
.Windows
.Automation
47 Imports System
.Windows
.Controls
48 Imports System
.Threading
49 Imports System
.Diagnostics
51 Imports System
.Windows
.Threading
52 Imports System
.Windows
.Media
54 Imports System
.Security
.Permissions
56 Namespace InvokePatternSample
57 '''------------------------------------------------------------------------
59 ''' Interaction logic for WCP client and Win32 target
61 '''------------------------------------------------------------------------
63 Public Class InvokePatternApp
65 #Region
"Member variables"
66 Private clientWindow
As Window
67 Private rootElement
As AutomationElement
68 Private clientTreeViews() As StackPanel
69 Private statusText
As TextBlock
70 Private treeviewPanel
As StackPanel
71 Private elementInfoCompile
As StringBuilder
72 Private clientScrollViewer
As ScrollViewer
73 Private targetApp
As String
74 ' Delegates for updating the client UI based on target application events.
75 Private Delegate
Sub OutputDelegate(ByVal message
As String)
78 #Region
"Target listeners"
79 '''--------------------------------------------------------------------
81 ''' Listens for a structure changed event in the target.
83 ''' <param name="sender">The object that raised the event.</param>
84 ''' <param name="e">Event arguments.</param>
86 ''' Since the events are happening on a different thread than the
87 ''' client we need to use a Dispatcher delegate to handle them.
89 '''--------------------------------------------------------------------
90 Private Sub ChildElementsAdded(ByVal sender
As Object, ByVal e
As StructureChangedEventArgs
)
91 clientWindow
.Dispatcher
.BeginInvoke(DispatcherPriority
.Background
, New DispatcherOperationCallback(AddressOf NotifyChildElementsAdded
), Nothing)
92 End Sub 'ChildElementsAdded
95 '''--------------------------------------------------------------------
97 ''' The delegate for the structure changed event in the target.
99 ''' <param name="arg">null argument</param>
100 ''' <returns>A null object.</returns>
101 '''--------------------------------------------------------------------
102 Private Function NotifyChildElementsAdded(ByVal arg
As Object) As Object
103 FindTreeViewsInTarget()
106 End Function 'NotifyChildElementsAdded
109 '''--------------------------------------------------------------------
111 ''' Handles the window closed event in the target.
113 ''' <param name="sender">The object that raised the event.</param>
114 ''' <param name="e">Event arguments.</param>
115 '''--------------------------------------------------------------------
116 Private Sub onTargetClose(ByVal sender
As Object, ByVal e
As AutomationEventArgs
)
117 clientWindow
.Dispatcher
.BeginInvoke(DispatcherPriority
.Background
, New DispatcherOperationCallback(AddressOf CloseApp
), Nothing)
118 End Sub 'onTargetClose
121 '''--------------------------------------------------------------------
123 ''' The delegate for the window closed event in the target.
125 ''' <param name="arg">null argument</param>
126 ''' <returns>A null object.</returns>
127 '''--------------------------------------------------------------------
128 Private Function CloseApp(ByVal arg
As Object) As Object
131 End Function 'CloseApp
134 #Region
"Client setup"
136 '''--------------------------------------------------------------------
138 ''' Initialize both client and target applications.
140 ''' <param name="e">Startup arguments</param>
141 '''--------------------------------------------------------------------
142 Protected Overrides
Sub OnStartup(ByVal e
As StartupEventArgs
)
143 ' Create our informational window
146 ' Get the root element from our target application.
147 ' In general, you should try to obtain only direct children of
148 ' the RootElement. A search for descendants may iterate through
149 ' hundreds or even thousands of elements, possibly resulting in
150 ' a stack overflow. If you are attempting to obtain a specific
151 ' element at a lower level, you should start your search from the
152 ' application window or from a container at a lower level.
154 System
.Windows
.Forms
.Application
.StartupPath
+ "\InvokePatternTarget.exe"
155 rootElement
= StartApp(targetApp
)
156 If rootElement Is
Nothing Then
160 ' Add a structure change listener for the root element.
161 Dim structureChange
As _
162 New StructureChangedEventHandler(AddressOf ChildElementsAdded
)
163 Automation
.AddStructureChangedEventHandler( _
164 rootElement
, TreeScope
.Descendants
, structureChange
)
166 ' Iterate through the controls in the target application.
167 FindTreeViewsInTarget()
171 '''--------------------------------------------------------------------
173 ''' Start the target application.
175 ''' <param name="app">The target appliation.</param>
177 ''' Starts the application that we are going to use for as our
178 ''' root element for this sample.
180 '''--------------------------------------------------------------------
181 <SecurityPermission(SecurityAction
.Demand
, Flags
:=SecurityPermissionFlag
.UnmanagedCode
)> _
182 Private Function StartApp(ByVal app
As String) As AutomationElement
183 If (File
.Exists(app
)) Then
184 Dim targetElement
As AutomationElement
187 Dim p
As Process
= Process
.Start(app
)
189 ' Give application a second to startup.
192 targetElement
= AutomationElement
.FromHandle(p
.MainWindowHandle
)
193 If targetElement Is
Nothing Then
196 Dim targetClosedHandler
As _
197 New AutomationEventHandler(AddressOf onTargetClose
)
198 Automation
.AddAutomationEventHandler( _
199 WindowPattern
.WindowClosedEvent
, _
200 targetElement
, TreeScope
.Element
, targetClosedHandler
)
202 ' Set size and position of target.
203 Dim targetTransformPattern
As TransformPattern
= _
204 DirectCast(targetElement
.GetCurrentPattern(TransformPattern
.Pattern
), _
206 If (IsNothing(targetTransformPattern
)) Then
210 targetTransformPattern
.Resize(550, 400)
211 targetTransformPattern
.Move( _
212 clientWindow
.Left
+ clientWindow
.Width
+ 25, clientWindow
.Top
)
214 Output("Target started.")
216 ' Return the AutomationElement
220 Output((app
+ " not found."))
223 End Function 'StartApp
226 '''--------------------------------------------------------------------
228 ''' Creates a client window to which output may be written.
230 '''--------------------------------------------------------------------
231 Private Sub CreateWindow()
233 clientWindow
= New Window()
234 clientWindow
.Width
= 400
235 clientWindow
.Height
= 600
236 clientWindow
.Left
= 50
237 clientWindow
.Top
= 50
239 ' Define a ScrollViewer.
240 clientScrollViewer
= New ScrollViewer()
241 clientScrollViewer
.VerticalScrollBarVisibility
= ScrollBarVisibility
.Auto
242 clientScrollViewer
.HorizontalContentAlignment
= HorizontalAlignment
.Stretch
244 ' Add Layout control.
245 Dim clientStackPanel
As New StackPanel()
246 Dim statusPanel
As New StackPanel()
247 statusPanel
.HorizontalAlignment
= HorizontalAlignment
.Center
248 treeviewPanel
= New StackPanel()
250 statusText
= New TextBlock()
251 statusText
.FontWeight
= FontWeights
.Bold
252 statusText
.TextWrapping
= TextWrapping
.Wrap
254 ' Add child elements to the parent StackPanel
255 statusPanel
.Children
.Add(statusText
)
256 clientStackPanel
.Children
.Add(statusPanel
)
257 clientStackPanel
.Children
.Add(treeviewPanel
)
259 ' Add the StackPanel as the lone Child of the Border
260 clientScrollViewer
.Content
= clientStackPanel
262 ' Add the Border as the Content of the Parent Window Object
263 clientWindow
.Content
= clientScrollViewer
266 End Sub 'CreateWindow
269 '''--------------------------------------------------------------------
271 ''' Finds all TreeView controls in the target.
273 '''--------------------------------------------------------------------
274 Private Sub FindTreeViewsInTarget()
275 ' Initialize the client area used to report target controls.
276 treeviewPanel
.Children
.Clear()
277 ' Set up our search condition
278 Dim rootTreeViewCondition
As New _
279 PropertyCondition(AutomationElement
.ControlTypeProperty
, ControlType
.Tree
)
280 ' Find all controls that support the condition.
281 Dim targetTreeViewElements
As AutomationElementCollection
= _
282 rootElement
.FindAll(TreeScope
.Children
, rootTreeViewCondition
)
283 If targetTreeViewElements
.Count
<= 0 Then
284 Output("TreeView control(s) not found.")
287 ' Create an area in the client to report each TreeView in the target.
288 clientTreeViews
= New StackPanel(targetTreeViewElements
.Count
) {}
289 Dim elementIndex
As Integer
290 For elementIndex
= 0 To targetTreeViewElements
.Count
- 1
291 clientTreeViews(elementIndex
) = New StackPanel()
292 Dim clientTreeViewBorder
As New Border()
293 clientTreeViewBorder
.BorderThickness
= New Thickness(1)
294 clientTreeViewBorder
.BorderBrush
= Brushes
.Black
295 clientTreeViewBorder
.Margin
= New Thickness(5)
296 clientTreeViewBorder
.Background
= Brushes
.LightGray
297 clientTreeViewBorder
.Child
= clientTreeViews(elementIndex
)
298 treeviewPanel
.Children
.Add(clientTreeViewBorder
)
299 ' Iterate through the descendant controls of each TreeView.
300 FindTreeViewDescendants(targetTreeViewElements(elementIndex
), elementIndex
)
303 End Sub 'FindTreeViewsInTarget
306 '''--------------------------------------------------------------------
308 ''' Walks the UI Automation tree of the target and reports the control
309 ''' type of each element it finds in the control view to the client.
311 ''' <param name="targetTreeViewElement">
312 ''' The root of the search on this iteration.
314 ''' <param name="treeviewIndex">
315 ''' The TreeView index for this iteration.
318 ''' This is a recursive function that maps out the structure of the
319 ''' subtree beginning at the AutomationElement passed in as
320 ''' rootElement on the first call. This could be, for example,
321 ''' an application window.
322 ''' CAUTION: Do not pass in AutomationElement.RootElement. Attempting
323 ''' to map out the entire subtree of the desktop could take a very
324 ''' long time and even lead to a stack overflow.
326 '''--------------------------------------------------------------------
327 Private Sub FindTreeViewDescendants( _
328 ByVal targetTreeViewElement
As AutomationElement
, _
329 ByVal treeviewIndex
As Integer)
330 If (IsNothing(targetTreeViewElement
)) Then
334 Dim elementNode
As AutomationElement
= _
335 TreeWalker
.ControlViewWalker
.GetFirstChild(targetTreeViewElement
)
337 While Not (elementNode Is
Nothing)
338 Dim elementInfo
As New Label()
339 elementInfo
.Margin
= New Thickness(0)
340 clientTreeViews(treeviewIndex
).Children
.Add(elementInfo
)
342 ' Compile information about the control.
343 elementInfoCompile
= New StringBuilder()
344 Dim controlName
As String
345 If (elementNode
.Current
.Name
= "") Then
346 controlName
= "Unnamed control"
348 controlName
= elementNode
.Current
.Name
350 Dim autoIdName
As String
351 If (elementNode
.Current
.AutomationId
= "") Then
352 autoIdName
= "No AutomationID"
354 autoIdName
= elementNode
.Current
.AutomationId
358 elementInfoCompile
.Append(controlName
).Append(" (") _
359 .Append(elementNode
.Current
.ControlType
.LocalizedControlType
) _
360 .Append(" - ").Append(autoIdName
).Append(")")
362 ' Test for the control patterns of interest for this sample.
363 Dim objPattern
As Object = Nothing
364 Dim expcolPattern
As ExpandCollapsePattern
365 If True = elementNode
.TryGetCurrentPattern(ExpandCollapsePattern
.Pattern
, objPattern
) Then
366 expcolPattern
= DirectCast(objPattern
, ExpandCollapsePattern
)
367 If expcolPattern
.Current
.ExpandCollapseState
<> ExpandCollapseState
.LeafNode
Then
368 Dim expcolButton
As New Button()
369 expcolButton
.Margin
= New Thickness(0, 0, 0, 5)
370 expcolButton
.Height
= 20
371 expcolButton
.Width
= 100
372 expcolButton
.Content
= "ExpandCollapse"
373 expcolButton
.Tag
= expcolPattern
374 AddHandler expcolButton
.Click
, AddressOf ExpandCollapse_Click
375 clientTreeViews(treeviewIndex
).Children
.Add(expcolButton
)
378 Dim togPattern
As TogglePattern
379 If True = elementNode
.TryGetCurrentPattern(TogglePattern
.Pattern
, objPattern
) Then
380 togPattern
= DirectCast(objPattern
, TogglePattern
)
381 Dim togButton
As New Button()
382 togButton
.Margin
= New Thickness(0, 0, 0, 5)
383 togButton
.Height
= 20
384 togButton
.Width
= 100
385 togButton
.Content
= "Toggle"
386 togButton
.Tag
= togPattern
387 AddHandler togButton
.Click
, AddressOf Toggle_Click
388 clientTreeViews(treeviewIndex
).Children
.Add(togButton
)
390 Dim invPattern
As InvokePattern
391 If True = elementNode
.TryGetCurrentPattern(InvokePattern
.Pattern
, objPattern
) Then
392 invPattern
= DirectCast(objPattern
, InvokePattern
)
393 Dim invButton
As New Button()
394 invButton
.Margin
= New Thickness(0)
395 invButton
.Height
= 20
396 invButton
.Width
= 100
397 invButton
.Content
= "Invoke"
398 invButton
.Tag
= invPattern
399 AddHandler invButton
.Click
, AddressOf Invoke_Click
400 clientTreeViews(treeviewIndex
).Children
.Add(invButton
)
402 ' Display compiled information about the control.
403 elementInfo
.Content
= elementInfoCompile
404 Dim sep
As New Separator()
405 clientTreeViews(treeviewIndex
).Children
.Add(sep
)
407 ' Iterate to next element.
408 ' elementNode - Current element.
409 ' treeviewIndex - Index of parent TreeView.
410 FindTreeViewDescendants(elementNode
, treeviewIndex
)
411 elementNode
= TreeWalker
.ControlViewWalker
.GetNextSibling(elementNode
)
414 End Sub 'FindTreeViewDescendants
416 '''--------------------------------------------------------------------
418 ''' Handles the Toggle click event.
420 ''' <param name="sender">The object that raised the event.</param>
421 ''' <param name="e">Event arguments.</param>
422 '''--------------------------------------------------------------------
423 Private Sub Toggle_Click(ByVal sender
As Object, ByVal e
As RoutedEventArgs
)
424 Dim clientButton
As Button
= DirectCast(sender
, Button
)
425 Dim t
As TogglePattern
= DirectCast(clientButton
.Tag
, TogglePattern
)
426 If (IsNothing(t
)) Then
430 statusText
.Text
= "Element toggled " + t
.Current
.ToggleState
.ToString()
431 End Sub 'Toggle_Click
433 '''--------------------------------------------------------------------
435 ''' Handles the Invoke click event on the client control.
436 ''' The client click handler calls Invoke() on the equivalent target control.
438 ''' <param name="sender">The object that raised the event.</param>
439 ''' <param name="e">Event arguments.</param>
441 ''' The Tag property of the FrameworkElement, the client button in this
442 ''' case, is used to store the InvokePattern object previously obtained
443 ''' from the associated target control.
445 '''--------------------------------------------------------------------
446 Private Sub Invoke_Click(ByVal sender
As Object, ByVal e
As RoutedEventArgs
)
447 Dim clientButton
As Button
= DirectCast(sender
, Button
)
448 Dim targetInvokePattern
As InvokePattern
= _
449 DirectCast(clientButton
.Tag
, InvokePattern
)
450 If (IsNothing(targetInvokePattern
)) Then
453 targetInvokePattern
.Invoke()
454 statusText
.Text
= "Button invoked."
455 End Sub 'Invoke_Click
457 '''--------------------------------------------------------------------
459 ''' Handles the ExpandCollapse click event.
461 ''' <param name="sender">The object that raised the event.</param>
462 ''' <param name="e">Event arguments.</param>
463 '''--------------------------------------------------------------------
464 Private Sub ExpandCollapse_Click(ByVal sender
As Object, ByVal e
As RoutedEventArgs
)
465 Dim clientButton
As Button
= DirectCast(sender
, Button
)
466 Dim ec
As ExpandCollapsePattern
= _
467 DirectCast(clientButton
.Tag
, ExpandCollapsePattern
)
468 If (IsNothing(ec
)) Then
471 Dim currentState
As ExpandCollapseState
= ec
.Current
.ExpandCollapseState
473 If currentState
= ExpandCollapseState
.Collapsed _
474 OrElse currentState
= ExpandCollapseState
.PartiallyExpanded
Then
476 statusText
.Text
= "TreeItem expanded."
477 ElseIf currentState
= ExpandCollapseState
.Expanded
Then
479 statusText
.Text
= "TreeItem collapsed."
482 ' The current state of the element is LeafNode.
483 Output("Unable to expand or collapse the element.")
485 End Sub 'ExpandCollapse_Click
488 '''--------------------------------------------------------------------
490 ''' Handles the text changed event in the target.
492 ''' <param name="message">The feedback string to display.</param>
494 ''' Since the events are happening on a different thread than the
495 ''' client we need to use a Dispatcher delegate to handle them.
497 '''--------------------------------------------------------------------
498 Private Sub Output(ByVal message
As String)
499 clientWindow
.Dispatcher
.BeginInvoke( _
500 DispatcherPriority
.Background
, _
501 New OutputDelegate(AddressOf NotifyTargetEvent
), message
)
504 '''--------------------------------------------------------------------
506 ''' The delegate for the text changed event in the target.
508 ''' <param name="arg">string argument</param>
509 '''--------------------------------------------------------------------
510 Private Sub NotifyTargetEvent(ByVal arg
As String)
511 Dim message
As String = DirectCast(arg
, String)
512 statusText
.Text
= message
513 End Sub 'NotifyTextChanged
515 '''--------------------------------------------------------------------
517 ''' The main entry point for the application.
519 '''--------------------------------------------------------------------
521 Friend NotInheritable Class TestMain
524 ' Create an instance of the sample class
525 ' and call its Run() method to start it.
526 Dim app
As New InvokePatternApp()
531 '''--------------------------------------------------------------------
533 ''' Handles our application shutdown.
535 ''' <param name="e">Event arguments.</param>
536 '''--------------------------------------------------------------------
537 Protected Overrides
Sub OnExit(ByVal e
As ExitEventArgs
)
538 If Not (rootElement Is
Nothing) Then
539 Automation
.RemoveAllEventHandlers()
545 End Class
'InvokePatternApp '