views:

381

answers:

2

I am using Microsoft UI Automation (i.e. AutomationElement) to run automated acceptance tests against my application. This has gone well, but I've hit a situation that doesn't appear to be exposed to the automation framework.

I have an ItemsControl (although I could be using one of its derived controls, e.g. ListBox) and I am using CollectionViewSource to group items. Here is a complete window to demonstrate:

<Window x:Class="GroupAutomation.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Orchestra">
    <Window.Resources>

        <!-- Take some simple data -->
        <XmlDataProvider x:Key="SampleData" XPath="Orchestra/Instrument">
            <x:XData>
                <Orchestra xmlns="">
                    <Instrument Name="Flute" Category="Woodwind" />
                    <Instrument Name="Trombone" Category="Brass" />
                    <Instrument Name="French horn" Category="Brass" />
                </Orchestra>
            </x:XData>
        </XmlDataProvider>

        <!-- Add grouping -->
        <CollectionViewSource Source="{Binding Source={StaticResource SampleData}}" x:Key="GroupedView">
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="@Category" />
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <!-- Show it in an ItemsControl -->
    <ItemsControl ItemsSource="{Binding Source={StaticResource GroupedView}}" HorizontalAlignment="Left" Margin="4">
        <ItemsControl.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" />
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </ItemsControl.GroupStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border Padding="4" Margin="4" Background="#FFDEDEDE">
                    <StackPanel>
                        <Label Content="{Binding XPath=@Name}" />
                        <Button Content="Play" />
                    </StackPanel>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

This produces a window containing the items grouped into their categories, and each item has a button that I'd like to click with UI Automation:

Screenshot of window with a list

However, if I look in UISpy.exe (or navigate with AutomationElement) I only see the groups (even in the Raw view):

UISpy

As you can see, the groups are there but they contain no items, so there is nowhere to look for the buttons. I have tried this in both WPF 3.5 SP1 and WPF 4.0 and get the same result.

Is it possible to use UI Automation on items that are grouped, and if so, how?

A: 

What tools are you using to write the automated scripts? I would have thought there'd be an option to drill into WPF's logical/visual trees rather than relying on the Win32 tree (as surfaced by UISpy).

If you have a look at the same application using Snoop, you'll see the full visual and logical trees.

HTH,
Kent

Kent Boogaart
I'm writing acceptance tests using MbUnit (but could be NUnit, xUnit.NET or whatever) and driving the application using the UI Automation framework. This decouples the tests from the implementation (to some extent) by dealing with UI items that the user can see and/or interact with rather than directly looking at the WPF trees. I've been doing this for about a year and this is the first case I've found that doesn't "just work," so I'm hoping to plug the gap rather than switch frameworks at this stage.
GraemeF
A: 

I'm not 100% sure about buttons, but TextBlock controls that are inside DataTemplates do not get put into the UI Automation tree. Apparently this is an optimization to avoid 1000's of unneccessary textblocks.

You can work around it by SubClassing TextBlock. Here's mine:

public class AutomatableTextBlock : TextBlock
{
    protected override AutomationPeer OnCreateAutomationPeer()
    {
        return new AutomatableTextBlockAutomationPeer(this);
    }

    class AutomatableTextBlockAutomationPeer : TextBlockAutomationPeer
    {
        public AutomatableTextBlockAutomationPeer(TextBlock owner)
            : base(owner)
        { }

        protected override bool IsControlElementCore()
        { return true; }
    }
}

Note: UI Automation also doesn't expose various other controls like Canvas, Panel, you can get them to show up with a similar subclass.

In saying that, I'm not sure why the Button isn't appearing.... Hrmmm

Orion Edwards
The elements I need show up fine if grouping is disabled, so I don't think your answer helps I'm afraid. I used a button in the example for clarity as they always show up (except in this case!).
GraemeF