2

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?

Inspector

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
Jimi
  • 29,621
  • 8
  • 43
  • 61

1 Answers1

3

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.

  • The Integer Key represents the position of an Item in the original ListView
  • The ListViewItem is a .Net object generated from the array of values (as strings) extracted from each item.

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
Jimi
  • 29,621
  • 8
  • 43
  • 61