ContentControl не виден, когда приложение запускается с помощью теста автоматизации пользовательского интерфейса, но он виден, когда приложение запускается пользователем.

Мы используем призму и WPF для создания приложения. Недавно мы начали использовать UI Automation (UIA) для тестирования нашего приложения. Но какое-то странное поведение произошло, когда мы запускаем тест МАУ. Вот упрощенная оболочка:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>    
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <TextBlock 
        Grid.Row="0" Grid.Column="0"
        Name="loadingProgressText"
        VerticalAlignment="Center" HorizontalAlignment="Center"
        Text="Loading, please wait..."/>

    <Border
        Grid.Row="0" 
        x:Name="MainViewArea">
        <Grid>
            ...
        </Grid>
    </Border>

    <!-- Popup -->
    <ContentControl 
        x:Name="PopupContentControl"
        Grid.Row="0" 
        prism:RegionManager.RegionName="PopupRegion"
        Focusable="False">
    </ContentControl>

    <!-- ErrorPopup -->
    <ContentControl 
        x:Name="ErrorContentControl"
        Grid.Row="0" 
        prism:RegionManager.RegionName="ErrorRegion"
        Focusable="False">
    </ContentControl>
</Grid>

В нашем приложении мы используем слои (Popup а такжеErrorPopup) прятатьсяMainViewArea, чтобы запретить доступ к элементам управления. ПоказыватьPopupМы используем следующий метод:

    //In constructor of current ViewModel we store _popupRegion instance to the local variable:
    _popupRegion = _regionManager.Regions["PopupRegion"];
    //---

    private readonly Stack<UserControl> _popups = new Stack<UserControl>();
    public void ShowPopup(UserControl popup)
    {
        _popups.Push(popup);

        _popupRegion.Add(PopupView);
        _popupRegion.Activate(PopupView);
    }

    public UserControl PopupView
    {
        get
        {
            if (_popups.Any())
                return _popups.Peek();
            return null;
        }
    }

Подобно этому, мы показываемErrorPopup по всем элементам нашего приложения:

    // In constructor we store _errorRegion:
    _errorRegion = _regionManager.Regions["ErrorRegion"]
    // --- 

    private UserControl _error_popup;

    public void ShowError(UserControl popup)
    {
        if (_error_popup == null)
        {
            _error_popup = popup;
            _errorRegion.Add(_error_popup);
            _errorRegion.Activate(_error_popup);
        }
    }

Mistics...

Когда мы запускаем его так, как это делают пользователи (двойной щелчок по значку приложения), мы видим оба пользовательских элемента управления (используяAutomationElement.FindFirst метод или черезVisual UI Automation Verify). Но когда мы запускаем его с помощью теста автоматизации пользовательского интерфейса -ErrorPopup исчезает из дерева элементов управления. Мы пытаемся запустить приложение так:

System.Diagnostics.Process.Start(pathToExeFile);

Я думаю, что мы что-то упустили. Но что?

Edit #1

Как сказал @chrismead, мы пытались запустить наше приложение сUseShellExecute флаг установлен в true, но это не помогает. Но если мы начнем приложение сcmd линии и вручную нажмите кнопку,Popup а такжеErrorPopup видны в дереве элементов управления автоматизации.

    Thread appThread = new Thread(delegate()
        {
            _userAppProcess = new Process();
            _userAppProcess.StartInfo.FileName = pathToExeFile;
            _userAppProcess.StartInfo.WorkingDirectory = System.IO.Directory.GetCurrentDirectory();
            _userAppProcess.StartInfo.UseShellExecute = true;
            _userAppProcess.Start();

        });
        appThread.SetApartmentState(ApartmentState.STA);
        appThread.Start();

Одно из наших предложений, когда мы используем методFindAll или жеFindFirst чтобы выполнить поиск по нажатой кнопке, окно как-то кэширует свое состояние автоматизации пользовательского интерфейса и не обновляет его.

Edit #2 Мы нашли, что метод расширения библиотеки призмыIRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) есть странное поведение. Если мы перестанем его использовать, это решит нашу проблему в частности. Теперь мы можем видеть ErrorView и любой вид вPopupContentControlи приложение обновляет древовидную структуру элементов МСА. Но это не ответ - "Просто прекратите использовать эту функцию"!

ВMainViewArea у нас естьContentControl, который обновляет его содержимое в зависимости от действий пользователя, и мы можем видеть только первый загруженныйUserControl к этомуContentControl.Content имущество. Это выполняется так:

IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.MainContentRegion, this.Uri);

И если мы изменим представление, в дереве автоматизации пользовательского интерфейса обновления не будут выполняться - вместо него появится первое загруженное представление. Но визуально мы наблюдаем другоеView, а такжеWPFInspector показывает это правильно (его показывать не дерево автоматизации пользовательского интерфейса), но Inspect.exe - нет.

Также наше предложение о том, что окно использует какое-то кэширование, неверно - кэширование в клиенте UI Automation мы должны включить явно, но мы этого не делаем.

 chrismead13 июн. 2012 г., 17:04
Вы пытались запустить приложение из окна cmd? Если это работает, то использование флага ProcessStartInfo.UseShellExecute может работать.
 stukselbax13 июн. 2012 г., 17:02
Да, это правильно. Но мы попробовали 3 способа запустить приложение из кода - никто не поможет нам найти правильное решение ...
 stukselbax13 июн. 2012 г., 17:06
Мы попробуем, когда придет следующий рабочий день :) Спасибо за предложение
 stukselbax14 июн. 2012 г., 07:26
Мы попробовали ваше предложение. Это не помогает.
 chrismead13 июн. 2012 г., 16:46
Итак, правильно ли говорить, что простой запуск приложения двойным щелчком приводит к тому, что элемент управления находится в дереве, а запуск Process.Start - нет?

Ответы на вопрос(3)

Решение Вопроса

что я пропустил некоторые детали, это было ключом к ответу. Я думаю, что это не было важной вещью. Тем не мение.

Мы использовалиNavBar отDevExpress управляет библиотекой дляWPF, Что получается, когдаNavBar присутствует, динамически созданные представления не отображаются в дереве автоматизации пользовательского интерфейса. При удалении его из окна появилась возможность видеть все динамически загруженные представления. Что этоNavBar - все еще мистик для меня.

Вот яркий пример, чтобы увидеть, что произошло, если NavBar присутствует или отсутствует в окне (требуется DevExpress).

MainWindow.xaml:

<Window xmlns:dxn="http://schemas.devexpress.com/winfx/2008/xaml/navbar"
        x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        >
    <Grid Name="ContentGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <!--Comment NavBar to see dynamic control in UI Automation tree-->
        <dxn:NavBarControl Name="asdasd">
            <dxn:NavBarControl.Groups>
                <dxn:NavBarGroup Header="asdasdasdasd" />
            </dxn:NavBarControl.Groups>
        </dxn:NavBarControl>
        <TextBox Grid.Column="1" Name="Statictb" Text="static is visible in ui automation tree" />
        <Button Grid.Row="1" Content="Create controls" Height="25"  Click="Button_Click"/>
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        TextBox tb = new TextBox();
        Grid.SetRow(tb, 1);
        Grid.SetColumn(tb, 1);
        tb.Text = "dynamic is not visible, if NavBar here...";
        ContentGrid.Children.Add(tb);
    }
}

Edit

СогласноDevExpress ответ на их сайте поддержки:

After a peer is created, listening of automation events may cause performance issues. We have decided to clear invocation lists of automation events to resolve it. In your specific situation, you need to disabling clearing. To do it, please set the static DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled property to False in the Window constructor.

Это решит проблему.

 29 июн. 2016 г., 19:57
Спасибо тебе за это. Мы потратили слишком много времени, пытаясь выяснить причину этого.

попробуйте найти последовательность нажатий клавиш (TAB и, скорее всего, ENTER), чтобы нажать кнопку, позволяющую увидеть элементы. это довольно легко отправлять нажатия клавиш, и я могу добавить здесь об этом, если это работает для вас. вы всегда можете установить порядок вкладок в вашем приложении, который наиболее удобен для пользователей.

------ Обновление от 20.06.12 --------

Пробовали ли вы дважды щелкнуть ярлык приложения на рабочем столе, используя PInvoke, чтобы увидеть, сможете ли вы увидеть элементы управления, когда оно открывается таким образом? Вот ссылка на пример здесь на stackoverflow:

Направление событий мыши [DllImport (& quot; user32.dll & quot;)] щелчок, двойной щелчок

Другая идея: некоторые элементы управления в приложении, которое я в настоящее время автоматизирую, не отображаются в дереве, пока на них не произойдет щелчок мышью. Чтобы выполнить это без использования каких-либо жестко закодированных координат, я нахожу что-то в дереве, которое является просто (выше / ниже / и т. Д.) Местом, где мне нужно нажать, чтобы появился элемент управления. Затем я получаю координаты мыши для этого элемента и помещаю мышь с небольшим смещением оттуда и щелкаю. Тогда я могу найти свои элементы управления в дереве. Если размер приложения изменяется, перемещается и т. Д., Это все равно будет работать, поскольку небольшое смещение все еще действует.

 21 июн. 2012 г., 15:59
Подумать об этом. Возможно, вы захотите начать с использования Process.Start с ярлыком вместо реального exe, чтобы посмотреть, поможет ли это.
 stukselbax20 июн. 2012 г., 08:42
Мы попробовали это - это не помогло.
 20 июн. 2012 г., 16:11
Добавлены некоторые дополнительные вещи выше ...

ContentControlодноранговый узел автоматизации должен обновить своих дочернихAutomationPeer.ResetChildrenCache() после того, как вид был изменен.

AutomationPeer.InvalidatePeer() должен иметь тот же эффект (в дополнение к другим побочным эффектам), и он должен вызываться автоматически в ответ наLayoutUpdated событие. Возможно, вы захотите проверить, что событие LayoutUpdated возникает при изменении представления.

 02 янв. 2014 г., 21:57
Большое спасибо за это - я боролся в течение 24 часов с подобной проблемой, гдеAutomationElementс внезапно исчезли. Прочитав ваш ответ, я проверил,AutomationPeer дети данного контроля были воссозданы во времяLayoutUpdated (а их не было) - так ваше предложение с призывомResetChildrenCache() а такжеInvalidatePeer() сделал свое дело. Еще раз спасибо. +1.

Ваш ответ на вопрос