如何在对项目进行分组的WPF ItemsControl上使用UI自动化?

时间:2010-05-05 09:48:41

标签: wpf xaml itemscontrol ui-automation

我正在使用Microsoft UI Automation(即AutomationElement)对我的应用程序运行自动验收测试。这很顺利,但我遇到的情况似乎没有暴露给自动化框架。

我有ItemsControl(虽然我可以使用其衍生控件之一,例如ListBox),但我使用CollectionViewSource对项目进行分组。这是一个完整的窗口来演示:

<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>

这会生成一个窗口,其中包含按类别分组的项目,每个项目都有一个我想用UI Automation点击的按钮:

Screenshot of window with a list http://pics.brizzly.com/thumb_lg_2C45.jpg

但是,如果我查看UISpy.exe(或使用AutomationElement导航),我只能看到这些组(即使在原始视图中):

UISpy http://pics.brizzly.com/thumb_lg_2C47.jpg

正如您所看到的,这些群组在那里,但它们不包含任何项目,因此无处可寻找按钮。我在WPF 3.5 SP1和WPF 4.0中都试过这个并获得相同的结果。

是否可以对分组的项目使用UI自动化,如果是,如何使用?

4 个答案:

答案 0 :(得分:5)

我遇到了这个问题并设法通过从https://developers.google.com/drive/v2/web/publish-site实施'GenericAutomationPeer'并为GroupItem添加特殊案例来解决此问题。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Media;
using System.Xml;

namespace ClassLibrary1
{
    public class MyItemsControl : ItemsControl
    {
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new GenericAutomationPeer(this);
        }
    }

    public class GenericAutomationPeer : UIElementAutomationPeer
    {
        public GenericAutomationPeer(UIElement owner) : base(owner)
        {
        }

        protected override List<AutomationPeer> GetChildrenCore()
        {
            var list = base.GetChildrenCore();
            list.AddRange(GetChildPeers(Owner));
            return list;
        }

        private List<AutomationPeer> GetChildPeers(UIElement element)
        {
            var list = new List<AutomationPeer>();
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
            {
                var child = VisualTreeHelper.GetChild(element, i) as UIElement;
                if (child != null)
                {
                    AutomationPeer childPeer;
                    if (child is GroupItem)
                    {
                        childPeer = new GenericAutomationPeer(child);
                    }
                    else
                    {
                        childPeer = UIElementAutomationPeer.CreatePeerForElement(child);
                    }
                    if (childPeer != null)
                    {
                        list.Add(childPeer);
                    }
                    else
                    {
                        list.AddRange(GetChildPeers(child));
                    }
                }
            }
            return list;
        }
    }

}

我希望这有助于任何人仍在寻找答案!

答案 1 :(得分:2)

我不是百分之百确定按钮,但是TextBlock内的DataTemplate控件会被放入UI Automation树中。显然,这是一个优化,以避免1000个不必要的文本块。

您可以通过SubClassing TextBlock来解决它。这是我的:

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; }
    }
}

注意:UI自动化也不会公开各种其他控件,例如CanvasPanel,您可以让它们显示类似的子类。

在说这个时,我不确定为什么Button没有出现...... Hrmmm

答案 2 :(得分:2)

我最终在我的应用程序中使用TreeWalker.RawViewWalker在使用AutomationElement.FindFirst找到模板后手动导航树来解决此问题。 FindFirst似乎可以在自动化其他人的应用程序时可靠地排除您想要的所有信息。当元素出现在“检查对象”中时,RawViewWalker似乎有效,但在UISpy或您的应用程序中则不行。

答案 3 :(得分:1)

您使用哪些工具编写自动脚本?我原本以为可以选择钻进WPF的逻辑/可视树而不是依赖Win32树(由UISpy表示)。

如果您使用Snoop查看同一个应用程序,您将看到完整的视觉和逻辑树。