I'm trying to retrieve data from a SysListView32 object with the code below but it's returning an empty string.
The elements that I need to retrieve are those highlighted in red, as well as the others contained in the other ControlType.ListItem
elements, according to the Inspector's print.
Can some one check what's wrong with my code?
Msgbox("Position the mouse cursor on the screen and press ENTER.")
Dim pt As POINTAPI
GetCursorPos(pt)
Dim hwnd As IntPtr = WindowFromPoint(pt)
Dim hwnd As IntPtr = 67202
Dim el As AutomationElement = AutomationElement.FromHandle(hwnd)
Dim walker As TreeWalker = TreeWalker.ContentViewWalker
Dim i As Integer = 0
Dim child As AutomationElement = walker.GetFirstChild(el)
While child IsNot Nothing
'
Dim k As Integer = 0
Dim child2 As AutomationElement = walker.GetFirstChild(child)
While child2 IsNot Nothing
Console.WriteLine(child2.Current.ToString)
child2 = walker.GetNextSibling(child2)
End While
child = walker.GetNextSibling(child)
End While
The SysListView32 may not provide the information requested if its current view state is not LV_VIEW_DETAILS
, so we should temporarily (if the current view state is different), use the MultipleViewPattern of its AutomationElement to verify the view state and change it if necessary using the MultipleViewPattern.SetCurrentView() method.
The SetCurrentView()
method uses the same values of the Win32 Control.
Then use the AutomationElement FindAll() method of the to find all child elements of type ControlType.DataItem or ControlType.ListItem
(using an OrCondition).
For each of them, get all child items of type ControlType.Edit
and ControlType.Text
(using another OrCondition
).
The position of each Item in the list is retrieved using the Item's GridItemPattern, to access the Item's Row property.
Finally, we restore the previous View State if we had to change it.
The code in the example fills a Dictionary(Of Integer, ListViewItem)
(named sysListViewItems
here), containing all the Items extracted from the SysListView32.
If you don't need ListViewItem objects, you can just store the List(Of String)
, represented by the itemsText
object, instead of creating a ListViewItem here:
sysListViewItems.Add(gridPattern.Current.Row, New ListViewItem(itemsText.ToArray())).
The Handle of a SysListView32 can also be acquired enumerating the children of its Top Level Window by ClassName
.
AutomationElement.RootElement provides the current Desktop Element:
Dim parentWindow = AutomationElement.RootElement.FindFirst(
TreeScope.Children, New PropertyCondition(AutomationElement.NameProperty, "[Window Caption]"))
Dim sysListView32 = parentWindow.FindAll(
TreeScope.Subtree, New PropertyCondition(AutomationElement.ClassNameProperty, "SysListView32"))
If more than one SysListView32 is found, filter by Header content, direct parent ControlType
or ClassName
or anything else that allows to single it out.
UI Automation requires a reference to the UIAutomationClient
and UIAutomationTypes
assemblies.
Imports System.Windows.Automation
' Find the ListView Handle as described or the ListView UI Element directly
Dim sysListViewHandle = [GetSysListView32Handle()]
Dim sysListViewElement = AutomationElement.FromHandle(sysListViewHandle)
If sysListViewElement Is Nothing Then Return
Dim sysListViewItems = New Dictionary(Of Integer, ListViewItem)()
Dim mulView As MultipleViewPattern = Nothing
Dim pattern As Object = Nothing
Dim currentView As Integer = -1
If sysListViewElement.TryGetCurrentPattern(MultipleViewPattern.Pattern, pattern) Then
mulView = DirectCast(pattern, MultipleViewPattern)
currentView = mulView.Current.CurrentView
If currentView <> ListViewWState.LV_VIEW_DETAILS Then
mulView.SetCurrentView(ListViewWState.LV_VIEW_DETAILS)
End If
End If
Dim childItemsCondition = New OrCondition(
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.DataItem),
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem))
Dim childItems = sysListViewElement.FindAll(TreeScope.Children, childItemsCondition)
If childItems.Count = 0 Then Return
Dim subItemsCondition = New OrCondition(
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit),
New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text))
For Each item As AutomationElement In childItems
Dim itemsText = New List(Of String)()
Dim subItems = item.FindAll(TreeScope.Children, subItemsCondition)
For Each subItem As AutomationElement In subItems
itemsText.Add(subItem.Current.Name)
Next
Dim gridPattern = DirectCast(subItems(0).GetCurrentPattern(GridItemPattern.Pattern), GridItemPattern)
sysListViewItems.Add(gridPattern.Current.Row, New ListViewItem(itemsText.ToArray()))
Next
If mulView IsNot Nothing Then
mulView.SetCurrentView(currentView)
End If
Friend Enum ListViewWState
LV_VIEW_ICON = &H0
LV_VIEW_DETAILS = &H1
LV_VIEW_SMALLICON = &H2
LV_VIEW_LIST = &H3
LV_VIEW_TILE = &H4
End Enum