Problem with ListItems as Childs of Window in Wpf App

Feb 8, 2010 at 11:00 AM

I am using a third party control that composes a ComboBox out of a textBox, a button and a popup that displays the ComboBox items when the button is clicked. This popup can only be retrieved as a window by calling Application.GetWindows() and then selecting the one thats title property is different to the mainWindow title (not very elegant, hints to improve this are welcome) since it is not accessible as window.PopupMenu()/.Popup.

The popup is the Wpf ItemsHost and so i get ListItems as direct children of a window when logging the structure of the new (popup)window. When trying to retrieve these ListItems by calling popup.Get<ListItem>() i only get the first ListItem, when trying to use an indexed accessor (popup.Get(SearchCriteria.ByControlType(typeof (ListItem)).AndIndex(1)) ) i don't get any items since an indexed popup.Get() is only supported for PrimaryUIItems. Unfortunatley ListItem is a secondaryUIItem.

LogStructureCode of "ComboBox" (= Button and TextBox, does not have a "parent Pane" or sth like that to identify this "ComboBox" as a CustomUIItem):

AutomationId: PART_EditableTextBox
    ControlType: ControlType.Edit
    Name:
    HelpText:
    Bounding rectangle: 532;105;180;21
    ClassName: TextBox
    IsOffScreen: False
    FrameworkId: WPF
    ProcessId: 6124

    System.Windows.Automation.ValuePattern
    System.Windows.Automation.ScrollPattern
    System.Windows.Automation.TextPattern

        AutomationId: VerticalScrollBar
        ControlType: ControlType.ScrollBar
        Name:
        HelpText:
        Bounding rectangle: Empty
        ClassName: ScrollBar
        IsOffScreen: True
        FrameworkId: WPF
        ProcessId: 6124

        System.Windows.Automation.RangeValuePattern

        AutomationId: HorizontalScrollBar
        ControlType: ControlType.ScrollBar
        Name:
        HelpText:
        Bounding rectangle: Empty
        ClassName: ScrollBar
        IsOffScreen: True
        FrameworkId: WPF
        ProcessId: 6124

        System.Windows.Automation.RangeValuePattern

    AutomationId: PART_DropDownButton
    ControlType: ControlType.Button
    Name:
    HelpText:
    Bounding rectangle: 712;105;20;21
    ClassName: Button
    IsOffScreen: False
    FrameworkId: WPF
    ProcessId: 6124

    System.Windows.Automation.TogglePattern

        AutomationId:
        ControlType: ControlType.Button
        Name:
        HelpText:
        Bounding rectangle: 712;105;20;21
        ClassName: Button
        IsOffScreen: False
        FrameworkId: WPF
        ProcessId: 6124

        System.Windows.Automation.InvokePattern

 

 

LogStructureCode of popup:

AutomationId:
ControlType: ControlType.Window
Name:
HelpText:
Bounding rectangle: 532;82;200;64
ClassName: Popup
IsOffScreen: False
FrameworkId: WPF
ProcessId: 2464


    AutomationId: PART_ClearButton
    ControlType: ControlType.Button
    Name:
    HelpText:
    Bounding rectangle: Empty
    ClassName: Button
    IsOffScreen: True
    FrameworkId: WPF
    ProcessId: 2464

    System.Windows.Automation.InvokePattern

    AutomationId:
    ControlType: ControlType.ListItem
    Name: Project.ViewModelType
    HelpText:
    Bounding rectangle: 537;89;190;24
    ClassName: ListBoxItem
    IsOffScreen: False
    FrameworkId: WPF
    ProcessId: 2464


    AutomationId:
    ControlType: ControlType.ListItem
    Name: Project.ViewModelType
    HelpText:
    Bounding rectangle: 537;114;190;24
    ClassName: ListBoxItem
    IsOffScreen: False
    FrameworkId: WPF
    ProcessId: 2464


    AutomationId: VerticalScrollBar
    ControlType: ControlType.ScrollBar
    Name:
    HelpText:
    Bounding rectangle: Empty
    ClassName: ScrollBar
    IsOffScreen: True
    FrameworkId: WPF
    ProcessId: 2464

    System.Windows.Automation.RangeValuePattern

    AutomationId: HorizontalScrollBar
    ControlType: ControlType.ScrollBar
    Name:
    HelpText:
    Bounding rectangle: Empty
    ClassName: ScrollBar
    IsOffScreen: True
    FrameworkId: WPF
    ProcessId: 2464

    System.Windows.Automation.RangeValuePattern

 

Does anybody have any suggestions how to solve this problem?

every idea would be appreciated, thanks

Coordinator
Feb 10, 2010 at 5:50 AM

Have you looked at the WPF Items section on UI Items page, which is intended to solve this specific problem?

 

Feb 10, 2010 at 1:02 PM

hi vivek. Yes i already tried this approach but the ListItems are not contained in the window.Items collection and

var items = window.Items.FindAll(s => s.AutomationElement.Current.ClassName.Equals("ListBoxItem"));

didn't return anything because the window.Items-Collection only contains the "PART_ClearButton". Then i looked at your Debug.Details(automationElement) - Code and by using

AutomationElementCollection children = window.AutomationElement.FindAll(TreeScope.Children, Condition.TrueCondition);

i can get the AutomationElements that are contained in the window and there i can find the ListItems... now i just have to convert the automationElement to a White ListItem... but how? After trying another approach with:

var children = window.ItemsWithin(window);

i get the ListItems i searched for as UIItems (created by the PrimaryUIItemFactory) that i can cast to ListItems... now everything works, thanks

Feb 10, 2010 at 1:52 PM

a simple way to get an UIItem from an Automation Elementis to use the constructor like this way

 

Window mywindow = application.GetWindow("MyWindow");

AutomationElement element = GetAutomationElementFromWindow();

 

ListItem item = new ListItem(element, myWindow.ActionListener);

 

Throndorin

Feb 11, 2010 at 7:46 AM

@Throndorin:

thanks for your reply! I tried this approach as well, but the constructor of ListItem (and i think of all other UIItems) is protected, so you either have to inherit from each UIItem type you want to create by your own or you use the approach that's used internally by white to create these instances (create an instance of PrimaryUIItemFactory with a new AutomationElementFinder and let the factory create these instances), but that is exactly what is done in ItemsWithin() (by using the UIItems own PrimaryUIItemFactory) - so i think this is the best way to do this. The only thing that slightly irritates me is the fact that this produces more UIItems than the Items-Property contains - shouldn't be that the same at least for a window? Or am i missing something and this is by design?

 

Feb 11, 2010 at 10:52 AM
Edited Feb 11, 2010 at 4:05 PM

Throndorin, i am sorry. I just realized that your approach can be done for most UIItems, but for the special case of an ListItem this is not possible out of the box.

Feb 11, 2010 at 12:26 PM

Sorry, I test this only with a few ones :) TextBox for example.

 

Can you post an example for ListItem to have an idea if other have the same problem.

 

Maybe this would be a good idea to add to documentation or is this already documented?

than please add a link here!

 

Feb 11, 2010 at 4:02 PM

We are using Telerik RadComboBox with a custom ControlTemplate that puts the ItemsPresenter for the ComboBox into a Popup. I don't think that our ControlTemplate is something unusual, the basic problem is that this custom WPFComboBox is not detected as an UIAutomation ComboBox (don't know why, perhaps our custom ControlTemplate destroyed the UIAutomation-Pattern thats needed to detect the ComboBox), so i had to look for the popup in the windows collection of my white application instance. The LogStructure result of the popup is displayed in the initial post.

To get the ListItems i basically do the following:

var windows = Application.GetWindows().Where(s => s.AutomationElement.Current.ClassName.Equals("Popup")).ToList();

var popupWindow = windows[subLevel];

var items = new List<ListItem>();

items.AddRange(popupWindow.ItemsWithin(popupWindow)
                                                   .Where(s => s.AutomationElement.Current.ControlType.Equals(ControlType.ListItem))
                                                   .Cast<ListItem>()
                                                   .ToList());

The important thing to mention is that there is opened a new popup everytime

items[itemIndex].Click();

is called. So you get 5 popup windows when calling Application.GetWindows() and having a nested Menu with four SubMenuLevels (and trying to click a leaf MenuItem). Thats why i have a subLevel static property for my class that is abstracting my custom ComboBox. You can see the use of popupWindow.ItemsWithin(popupWindow) that i described in an earlier post.

Hope it helps someone,

kidrocker