Как использовать автоматизацию пользовательского интерфейса в элементе управления WPF ItemsControl, который группирует элементы?

Я использую Microsoft UI Automation (т.е. AutomationElement < / a>), чтобы запустить автоматические приемочные тесты моего приложения. Все прошло хорошо, но я столкнулся с ситуацией, которая, похоже, не раскрывается в среде автоматизации.

У меня есть ItemsControl (хотя я мог бы использовать один из его производных элементов управления, например _ 3_), и я использую _ 4_ для группировки элементов. Вот полное окно для демонстрации:

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

Это создает окно, содержащее элементы, сгруппированные по их категориям, и каждый элемент имеет кнопку, которую я хотел бы нажать с помощью автоматизации пользовательского интерфейса:

Скриншот окна со списком
(источник: brizzly .com)

Однако, если я посмотрю в UISpy.exe (или перейти с помощью AutomationElement) Я вижу только группы (даже в режиме Raw):

UISpy
(источник: brizzly.com)

Как видите, группы есть, но в них нет элементов, поэтому искать кнопки негде. Я пробовал это как в WPF 3.5 SP1, так и в WPF 4.0 и получил тот же результат.

Можно ли использовать автоматизацию пользовательского интерфейса для сгруппированных элементов, и если да, то как?


person GraemeF    schedule 05.05.2010    source источник
comment
Используйте непосредственно элементы управления itemscontrol, как показано здесь. ссылка   -  person user1912383    schedule 20.12.2016


Ответы (4)


Я столкнулся с этой проблемой, и мне удалось ее решить, реализовав GenericAutomationPeer из http://www.colinsalmcorner.com/post/genericautomationpeer--helping-the-coded-ui-framework-find-your-custom-controls и добавив особый случай для GroupItems.

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

}

Надеюсь, это поможет всем, кто все еще ищет ответ!

person Trasvi    schedule 02.08.2016
comment
Это не идеально, но, учитывая усилия по исправлению AutomationPeer для сгруппированных элементов ItemsControl, это очень эффективный обходной путь. Спасибо! - person grzegorz_p; 10.11.2016

Я не уверен на 100% насчет кнопок, но TextBlock элементы управления, находящиеся внутри DataTemplates, не помещаются в дерево автоматизации пользовательского интерфейса. По-видимому, это оптимизация, позволяющая избежать тысяч ненужных текстовых блоков.

Вы можете обойти это, создав подкласс 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; }
    }
}

Примечание. Автоматизация пользовательского интерфейса также не предоставляет различные другие элементы управления, такие как Canvas, Panel, вы можете заставить их отображаться с помощью аналогичного подкласса.

Говоря это, я не уверен, почему Button не появляется .... Хмммм

person Orion Edwards    schedule 17.05.2010
comment
Элементы, которые мне нужны, отображаются нормально, если группировка отключена, поэтому я не думаю, что ваш ответ поможет, боюсь. Я использовал кнопку в примере для ясности, поскольку они всегда отображаются (кроме этого случая!). - person GraemeF; 17.05.2010

В конечном итоге я решил эту проблему в своем приложении, используя TreeWalker.RawViewWalker для ручной навигации по дереву после использования AutomationElement.FindFirst для поиска шаблона. FindFirst, кажется, надежно исключает всю необходимую информацию при автоматизации чужого приложения. RawViewWalker, кажется, работает, когда элементы отображаются в «Inspect Objects», но не в UISpy или вашем приложении.

person mdonoughe    schedule 17.11.2011
comment
Это сработало! Интересным фактом является то, что UISpy не смог найти дочерний узел на одном компьютере, но нашел на другом. Оба компьютера имеют одинаковые характеристики и установлено одинаковое программное обеспечение. Как бы то ни было, трюк с TreeWalker работал у обоих. - person SlavaGu; 12.03.2013

Какие инструменты вы используете для написания автоматизированных скриптов? Я бы подумал, что есть возможность углубиться в логические / визуальные деревья WPF, а не полагаться на дерево Win32 (как показано UISpy).

Если вы посмотрите на то же приложение с помощью Snoop, вы увидите полные визуальные и логические деревья.

person Kent Boogaart    schedule 05.05.2010
comment
Я пишу приемочные тесты, используя MbUnit (но это может быть NUnit, xUnit.NET или что-то еще) и управляю приложением с помощью инфраструктуры UI Automation. Это отделяет тесты от реализации (до некоторой степени), имея дело с элементами пользовательского интерфейса, которые пользователь может видеть и / или взаимодействовать с ними, а не напрямую смотреть на деревья WPF. Я занимаюсь этим около года, и это первый случай, когда я обнаружил, что он не просто работает, поэтому я надеюсь заполнить пробел, а не переключать фреймворки на этом этапе. - person GraemeF; 06.05.2010