Автоматизация проблемы с нажатием кнопки ToolStrip

Я пытаюсь автоматизировать приложение формы Windows с помощью vb.net. У меня нет исходного кода программы, которую я хотел бы автоматизировать.

Со Spy++ до сих пор я использовал дескрипторы для кнопок и нажимал кнопки без проблем. Однако я наткнулся на проблему с кнопками панели инструментов, и я изо всех сил пытался ее обойти. Эти кнопки скрыты или невидимы (из того, что я читал), и они не отображаются в spy++, поэтому у них нет дескриптора; Поэтому я не могу использовать ручку кнопки, чтобы щелкнуть ее, потому что она не существует.

Чтобы решить эту проблему, мне пришлось переместить курсор мыши в определенное место на экране, изменить положение окна и запустить событие щелчка мыши. Однако этот подход не самый лучший, поскольку мышь нельзя использовать во время выполнения кода автоматизации. Кто-нибудь может предложить альтернативу? Я безуспешно просматривал Интернет и этот сайт в поисках решения в течение трех дней.

Любая помощь будет оценена по достоинству.


person user2068871    schedule 13.02.2013    source источник
comment
Как реализованы ваши кнопки панели инструментов? они инкапсулированы элементом управления?   -  person Watki02    schedule 13.02.2013
comment
Если ваши кнопки панели инструментов скрыты или невидимы, как вы можете щелкнуть по ним с помощью события щелчка мыши?   -  person Watki02    schedule 14.02.2013


Ответы (2)


В общем, лучше расположить свой код так, чтобы было разделение между вашей кнопкой панели инструментов (или ЛЮБОЙ кнопкой, которую вы когда-либо использовали в интерфейсе) и вашей бизнес-логикой в ​​​​бэкенде. Если возможно, создайте там «шов кода», написав функцию, которая будет вызываться обработчиком нажатия кнопки, даже если он такой простой, как

(In VB)

Private Sub btnSomeButton_Click(ByVal sender As System.Object, _
                                ByVal e As System.EventArgs) _
                                Handles btnSomeButton.Click
     BusinessLogicClass.DoSomeButtonWork()
End Sub

Таким образом, вы можете легко вызывать функцию в других функциях, использовать их в сценариях автоматизации, писать для них модульные тесты и макеты, а также выполнять рефакторинг в одном месте вместо множества. Это называется «развязка». Это действительно превосходное решение, особенно если вы планируете использовать процесс в «пакетном режиме».

person Watki02    schedule 13.02.2013
comment
Привет, спасибо за ответ. Приложение, которое я пытаюсь автоматизировать, имеет кнопки, добавленные на панель инструментов обычным способом. В приложении также есть поля со списком на панели инструментов, но они появляются в spy++, поэтому я могу использовать дескрипторы для этих элементов для автоматизации. Кнопки на панели инструментов не отображаются в spy++. кстати, панель инструментов отображается как родительское окно в spy++. Я могу захватить дескрипторы для полей со списком, используя findwindowex, и могу захватить дескриптор панели инструментов, используя findwindow. - person user2068871; 13.02.2013
comment
Я мало что знаю о шпионе++. Я думаю, вам придется обратиться за помощью к кому-то еще, если вы настаиваете на использовании этого маршрута. Я добавил тег для шпиона ++ на ваш вопрос для вас. - person Watki02; 13.02.2013
comment
Пожалуйста, примите мои извинения. Я не хотел говорить о шпионе++. У меня нет возможности изменить код в приложении, которое я пытаюсь автоматизировать. Я был бы рад принять любое из ваших предложений, поскольку я все еще не понимаю, как я могу нажать кнопку на панели инструментов, не используя положение мыши и положение окна. - person user2068871; 14.02.2013
comment
Это довольно важная деталь, которую вы упустили :). - person Watki02; 14.02.2013
comment
Единственная другая идея, которую я имею, состоит в том, чтобы использовать различную программу. Для управления экраном/мышью вы можете попробовать AutoHotkey. - person Watki02; 14.02.2013

Я сам решил проблему!!! Я так счастлив... лол. Я не пробовал автогорячие клавиши, но решение, которое я нашел, хорошее, или, по крайней мере, я так думаю. Здесь дается отличное объяснение... http://msdn.microsoft.com/en-us/magazine/cc163288.aspx

В любом случае, решение заключается в том, что vb.net имеет библиотеку/справочник «Автоматизация пользовательского интерфейса», которую вы можете добавить в свой проект. Я видел, как это упоминалось несколько раз в Интернете, но не мог понять, как его импортировать (извините, я новичок в vb.net/coding). В любом случае, вы добавляете ссылку «Автоматизация пользовательского интерфейса» в свой проект, а затем импортируете ее. Затем вы можете использовать библиотеку, чтобы найти все элементы управления на экране. Это включает в себя все кнопки на панели инструментов.

Я добавил пример кода ниже, чтобы продемонстрировать, как я его решил. Я уверен, что это в некотором роде веревочное и, вероятно, могло бы быть сделано лучше, но это работает. Дайте мне знать, что вы думаете о коде/решении.

Чтобы запустить код, вам нужно знать индекс кнопки и индекс панели инструментов. Если вы переходите в режим отладки, вы можете увеличивать индекс вручную и проверять controlName, пока не появится правильная кнопка. В приведенном ниже примере мне нужно было найти родительский элемент (окно), затем дочерний элемент (панель инструментов), а затем дочерние элементы (все кнопки под панелью инструментов).

Imports system.windows.automation
Imports system.eventargs

'RoutedEvenArgs has to exist as a class so I declare it here...

Public class RoutedEventArgs Inherits EventArgs

end class

'my form code is all under one class - I'll probably break it up better
' but at the moment this is how it is

Public Class form1

Private Sub Button1_Click_1(ByVal sender As System.Object, _
                            ByVal e As System.EventArgs) Handles Button1.Click

'click the button of hte automation front end and 
' call the findtreeviewdescendants procedure

    FindTreeViewDescendants()

End Sub

Private Sub FindTreeViewDescendants()

'define the desktop as the rootelement as everywindow is a child of this element

Dim aedesktop As AutomationElement = AutomationElement.RootElement

'create an automationelementcollection variable to store the buttons

Dim aebuttons As AutomationElementCollection

'find the screen using the screen title 

aeform = aedesktop.FindFirst(TreeScope.Children, New PropertyCondition _
(AutomationElement.NameProperty, "Single Stock View"))

'find all the child controls (this brings back all controls including the toolstrip)

aebuttons = aeform.FindAll(TreeScope.Children, New PropertyCondition _(AutomationElement.IsControlElementProperty, True))

'create an automationelement to store the button and get information out of it

Dim a As AutomationElement

'each button, in the collection, has an index (incidentally the index number corresponds with the order in  which the window loads each of the elements into the window), in this case the toolstrip is index 1 as it's in the header of the screen

a = aebuttons.Item(1)

'get the child elements of the toolstrip element (something interesting is that in this case there were 19 elements but when you use findwindowex you only get back 4)

aebuttons = a.FindAll(TreeScope.Children, New PropertyCondition(AutomationElement.IsControlElementProperty, True))

'again use the index of the button to pull back the element information

a = aebuttons.Item(11)

'create a stringbuilder to store information about the element

Dim elementInfoCompile = New StringBuilder()

'identify the controlname, which in my case is the tooltip tag of the button (thus solving the problem of how I can find a button with an image instead of text in it)

Dim controlName As String
If (a.Current.Name = "") Then
controlName = "Unnamed control"
Else
controlName = a.Current.Name
End If

'identify the autoidname - which in all cases seemed to be null - I've no idea why but this didn't matter anyway

Dim autoIdName As String

If (a.Current.AutomationId = "") Then
autoIdName = "No AutomationID"
Else
autoIdName = a.Current.AutomationId
End If

'invoke a click of the button

InvokeControl(a)

End Sub 'FindTreeViewDescendants

'the rest is self-explanatory....

Private Sub InvokeControl(ByVal targetControl As AutomationElement)
Dim invokePattern As InvokePattern = Nothing

Try
invokePattern = _
DirectCast(targetControl.GetCurrentPattern(invokePattern.Pattern),  _
InvokePattern)
Catch e As ElementNotEnabledException
' Object is not enabled. 
Return
Catch e As InvalidOperationException
' Object doesn't support the InvokePattern control pattern 
Return
End Try

invokePattern.Invoke()

End Sub 'InvokeControl

End Class
person user2068871    schedule 14.02.2013