added samples
[windows-sources.git] / sdk / samples / WPFSamples / InvokePatternApp / visualbasic / client.vb
blob378aa4d33cee3420212d71896c1557cd2fd5ec5d
1 '*****************************************************************************
2 ' * File: InvokePatternApp.vb
3 ' *
4 '* Description:
5 '* This sample demonstrates the UI Automation ExpandCollapse, Invoke, and
6 '* Toggle control pattern classes.
7 '*
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.
12 '*
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.
16 '*
17 '* Notes:
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).
25 ' *
26 ' *
27 ' * This file is part of the Microsoft .NET Framework SDK Code Samples.
28 ' *
29 ' * Copyright (C) Microsoft Corporation. All rights reserved.
30 ' *
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.
34 ' *
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.
39 ' *
40 ' ****************************************************************************
42 Imports System
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
50 Imports System.Text
51 Imports System.Windows.Threading
52 Imports System.Windows.Media
53 Imports System.IO
54 Imports System.Security.Permissions
56 Namespace InvokePatternSample
57 '''------------------------------------------------------------------------
58 ''' <summary>
59 ''' Interaction logic for WCP client and Win32 target
60 ''' </summary>
61 '''------------------------------------------------------------------------
63 Public Class InvokePatternApp
64 Inherits Application
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)
76 #End Region
78 #Region "Target listeners"
79 '''--------------------------------------------------------------------
80 ''' <summary>
81 ''' Listens for a structure changed event in the target.
82 ''' </summary>
83 ''' <param name="sender">The object that raised the event.</param>
84 ''' <param name="e">Event arguments.</param>
85 ''' <remarks>
86 ''' Since the events are happening on a different thread than the
87 ''' client we need to use a Dispatcher delegate to handle them.
88 ''' </remarks>
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 '''--------------------------------------------------------------------
96 ''' <summary>
97 ''' The delegate for the structure changed event in the target.
98 ''' </summary>
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()
104 Return Nothing
106 End Function 'NotifyChildElementsAdded
109 '''--------------------------------------------------------------------
110 ''' <summary>
111 ''' Handles the window closed event in the target.
112 ''' </summary>
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 '''--------------------------------------------------------------------
122 ''' <summary>
123 ''' The delegate for the window closed event in the target.
124 ''' </summary>
125 ''' <param name="arg">null argument</param>
126 ''' <returns>A null object.</returns>
127 '''--------------------------------------------------------------------
128 Private Function CloseApp(ByVal arg As Object) As Object
129 clientWindow.Close()
130 Return Nothing
131 End Function 'CloseApp
132 #End Region
134 #Region "Client setup"
136 '''--------------------------------------------------------------------
137 ''' <summary>
138 ''' Initialize both client and target applications.
139 ''' </summary>
140 ''' <param name="e">Startup arguments</param>
141 '''--------------------------------------------------------------------
142 Protected Overrides Sub OnStartup(ByVal e As StartupEventArgs)
143 ' Create our informational window
144 CreateWindow()
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.
153 targetApp = _
154 System.Windows.Forms.Application.StartupPath + "\InvokePatternTarget.exe"
155 rootElement = StartApp(targetApp)
156 If rootElement Is Nothing Then
157 Return
158 End If
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()
168 End Sub 'OnStartup
171 '''--------------------------------------------------------------------
172 ''' <summary>
173 ''' Start the target application.
174 ''' </summary>
175 ''' <param name="app">The target appliation.</param>
176 ''' <remarks>
177 ''' Starts the application that we are going to use for as our
178 ''' root element for this sample.
179 ''' </remarks>
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
186 ' Start application.
187 Dim p As Process = Process.Start(app)
189 ' Give application a second to startup.
190 Thread.Sleep(2000)
192 targetElement = AutomationElement.FromHandle(p.MainWindowHandle)
193 If targetElement Is Nothing Then
194 Return Nothing
195 Else
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), _
205 TransformPattern)
206 If (IsNothing(targetTransformPattern)) Then
207 Return Nothing
208 End If
210 targetTransformPattern.Resize(550, 400)
211 targetTransformPattern.Move( _
212 clientWindow.Left + clientWindow.Width + 25, clientWindow.Top)
214 Output("Target started.")
216 ' Return the AutomationElement
217 Return targetElement
218 End If
219 Else
220 Output((app + " not found."))
221 Return Nothing
222 End If
223 End Function 'StartApp
226 '''--------------------------------------------------------------------
227 ''' <summary>
228 ''' Creates a client window to which output may be written.
229 ''' </summary>
230 '''--------------------------------------------------------------------
231 Private Sub CreateWindow()
232 ' Define a window.
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
264 clientWindow.Show()
266 End Sub 'CreateWindow
269 '''--------------------------------------------------------------------
270 ''' <summary>
271 ''' Finds all TreeView controls in the target.
272 ''' </summary>
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.")
285 Return
286 End If
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)
301 Next elementIndex
303 End Sub 'FindTreeViewsInTarget
306 '''--------------------------------------------------------------------
307 ''' <summary>
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.
310 ''' </summary>
311 ''' <param name="targetTreeViewElement">
312 ''' The root of the search on this iteration.
313 ''' </param>
314 ''' <param name="treeviewIndex">
315 ''' The TreeView index for this iteration.
316 ''' </param>
317 ''' <remarks>
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.
325 ''' </remarks>
326 '''--------------------------------------------------------------------
327 Private Sub FindTreeViewDescendants( _
328 ByVal targetTreeViewElement As AutomationElement, _
329 ByVal treeviewIndex As Integer)
330 If (IsNothing(targetTreeViewElement)) Then
331 Return
332 End If
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"
347 Else
348 controlName = elementNode.Current.Name
349 End If
350 Dim autoIdName As String
351 If (elementNode.Current.AutomationId = "") Then
352 autoIdName = "No AutomationID"
353 Else
354 autoIdName = elementNode.Current.AutomationId
355 End If
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)
376 End If
377 End If
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)
389 End If
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)
401 End If
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)
412 End While
414 End Sub 'FindTreeViewDescendants
416 '''--------------------------------------------------------------------
417 ''' <summary>
418 ''' Handles the Toggle click event.
419 ''' </summary>
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
427 Return
428 End If
429 t.Toggle()
430 statusText.Text = "Element toggled " + t.Current.ToggleState.ToString()
431 End Sub 'Toggle_Click
433 '''--------------------------------------------------------------------
434 ''' <summary>
435 ''' Handles the Invoke click event on the client control.
436 ''' The client click handler calls Invoke() on the equivalent target control.
437 ''' </summary>
438 ''' <param name="sender">The object that raised the event.</param>
439 ''' <param name="e">Event arguments.</param>
440 ''' <remarks>
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.
444 ''' </remarks>
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
451 Return
452 End If
453 targetInvokePattern.Invoke()
454 statusText.Text = "Button invoked."
455 End Sub 'Invoke_Click
457 '''--------------------------------------------------------------------
458 ''' <summary>
459 ''' Handles the ExpandCollapse click event.
460 ''' </summary>
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
469 Return
470 End If
471 Dim currentState As ExpandCollapseState = ec.Current.ExpandCollapseState
473 If currentState = ExpandCollapseState.Collapsed _
474 OrElse currentState = ExpandCollapseState.PartiallyExpanded Then
475 ec.Expand()
476 statusText.Text = "TreeItem expanded."
477 ElseIf currentState = ExpandCollapseState.Expanded Then
478 ec.Collapse()
479 statusText.Text = "TreeItem collapsed."
480 End If
481 Catch
482 ' The current state of the element is LeafNode.
483 Output("Unable to expand or collapse the element.")
484 End Try
485 End Sub 'ExpandCollapse_Click
488 '''--------------------------------------------------------------------
489 ''' <summary>
490 ''' Handles the text changed event in the target.
491 ''' </summary>
492 ''' <param name="message">The feedback string to display.</param>
493 ''' <remarks>
494 ''' Since the events are happening on a different thread than the
495 ''' client we need to use a Dispatcher delegate to handle them.
496 ''' </remarks>
497 '''--------------------------------------------------------------------
498 Private Sub Output(ByVal message As String)
499 clientWindow.Dispatcher.BeginInvoke( _
500 DispatcherPriority.Background, _
501 New OutputDelegate(AddressOf NotifyTargetEvent), message)
502 End Sub 'Output
504 '''--------------------------------------------------------------------
505 ''' <summary>
506 ''' The delegate for the text changed event in the target.
507 ''' </summary>
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 '''--------------------------------------------------------------------
516 ''' <summary>
517 ''' The main entry point for the application.
518 ''' </summary>
519 '''--------------------------------------------------------------------
521 Friend NotInheritable Class TestMain
522 <STAThread()> _
523 Shared Sub Main()
524 ' Create an instance of the sample class
525 ' and call its Run() method to start it.
526 Dim app As New InvokePatternApp()
527 app.Run()
528 End Sub 'Main
529 End Class 'TestMain
531 '''--------------------------------------------------------------------
532 ''' <summary>
533 ''' Handles our application shutdown.
534 ''' </summary>
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()
540 End If
541 MyBase.OnExit(e)
542 End Sub 'OnExit
543 #End Region
545 End Class 'InvokePatternApp '
547 End Namespace