2012-06-13 8 views
9

Do budowy aplikacji używamy pryzmatu i WPF. Niedawno zaczęliśmy używać UI (Automation UI) do testowania naszej aplikacji. Jednak podczas uruchamiania testu UIA pojawiło się dziwne zachowanie. Oto uproszczony shell:ContentControl nie jest widoczny, gdy aplikacja uruchamia się za pomocą testu automatyzacji interfejsu użytkownika, ale jest widoczna po uruchomieniu aplikacji przez użytkownika

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

W naszej aplikacji, używamy warstwy (Popup i ErrorPopup), aby ukryć MainViewArea, aby zablokować dostęp do elementów sterujących. Aby pokazać Popup używamy kolejną metodę:

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

podobne do tego, pokażemy ErrorPopup nad wszystkimi elementami naszej aplikacji:

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

Kiedy prowadzimy w miarę jak użytkownicy to robią (dwukrotne kliknięcie ikony aplikacji), widzimy zarówno niestandardowe formanty (używając metody AutomationElement.FindFirst, jak i przez Visual UI Automation Verify). Ale gdy uruchomimy go, test automatyczny interfejsu użytkownika - ErrorPopup znika z drzewa elementów sterujących. Próbujemy uruchomić aplikację w następujący sposób:

System.Diagnostics.Process.Start(pathToExeFile); 

Myślę, że coś przeoczyliśmy. Ale co?

Edit # 1

Jak @chrismead powiedział, staraliśmy się uruchomić naszą aplikację z UseShellExecute flaga ustawiona na true, ale to nie pomaga. Ale jeśli uruchomimy aplikację z linii cmd, i ręcznie kliknij przycisk, Popup i ErrorPopup są widoczne w drzewie kontroli automatyzacji.

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(); 

Jeden z naszych sugestii jest, gdy używamy metody FindAll lub FindFirst szukać przycisk do kliknięcia, okno jakoś buforowane swój stan UI Automation, a nie aktualizuje go.

Edit # 2 Mamy dowiedzieć, że metoda rozszerzenie pryzmatu biblioteki IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) jakieś dziwne zachowanie. Jeśli przestaniemy z niego korzystać, rozwiąże to nasz szczególny problem. Teraz możemy zobaczyć ErrorView i dowolny widok w PopupContentControl, a także zaktualizować strukturę elementów aplikacji UIA. Ale to nie jest odpowiedź - "Po prostu przestań korzystać z tej funkcji"!

W MainViewArea mamy ContentControl, która aktualizuje zawartość w zależności od działań użytkownika, a my jesteśmy w stanie zobaczyć tylko pierwszy załadowane UserControl tej ContentControl.Content nieruchomości. Odbywa się to tak:

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

A jeśli zmienić widok, żadne aktualizacje będą przeprowadzane w UI Automation drzewa - pierwszy załadowany widok będzie w nim zamiast. Wizualnie obserwujemy jednak, że inny View i WPFInspector pokazuje go poprawnie (nie pokazuje drzewa automatyzacji interfejsu użytkownika), ale Inspect.exe - nie.

Również nasza sugestia, że ​​okno używa jakiegoś buforowania jest złe - buforowanie w kliencie automatyzacji interfejsu użytkownika musimy włączyć jawnie, ale nie robimy tego.

+1

Czy to słuszne stwierdzenie, że uruchomienie aplikacji z podwójnym kliknięciem powoduje, że kontrolka znajduje się w drzewie, ale proces Process.Start nie działa? – chrismead

+1

Tak, jest to poprawne. Ale próbowaliśmy uruchomić 3 aplikację z kodu - nikt nie jest w stanie nam pomóc w odpowiednim rozwiązaniu ... – stukselbax

+0

Czy próbowałeś uruchomić aplikację z okna cmd? Jeśli to działa, użycie flagi ProcessStartInfo.UseShellExecute może działać. – chrismead

Odpowiedz

7

Przykro mi, że straciłem trochę szczegółów, to był klucz do odpowiedzi. Myślę, że to nie było ważne. Tak czy inaczej.

Użyliśmy NavBar z DevExpress kontroli biblioteki dla WPF. Okazuje się, że gdy występuje NavBar, dynamicznie tworzone widoki nie pojawiają się w drzewie automatyzacji interfejsu użytkownika. Po usunięciu go z okna była możliwość zobaczenia wszystkich dynamicznie ładowanych widoków. Co jest dla mnie wciąż mistic?

Oto jasny przykład, aby zobaczyć, co się stało, jeśli NavBar jest obecny lub nieobecny w oknie (wymagana jest 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

Według DevExpress answer na swojej stronie wsparcia:

Po utworzeniu elementu równorzędnego odsłuchiwanie zdarzeń automatyzacji może powodować problemy z wydajnością. Zdecydowaliśmy się wyczyścić listy wywołań zdarzeń automatyzacji, aby je rozwiązać.W Twojej konkretnej sytuacji musisz wyłączyć czyszczenie. Aby to zrobić, ustaw statyczną właściwość DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled na False w konstruktorze Window.

Rozwiązuje to problem.

+0

Dziękuję za to. Zbyt dmuchnęliśmy za dużo czasu, próbując znaleźć przyczynę tego. – Jordan

1

stukselbax, spróbuj znaleźć sekwencję klawiszy (TAB i ENTER), aby kliknąć przycisk, który umożliwia wyświetlanie pozycji. wysyłanie naciśnięć klawiszy jest bardzo proste i mogę dodać tutaj więcej informacji, jeśli to działa. zawsze możesz ustalić kolejność kart w aplikacji, która ma największy sens dla użytkowników.

------ Aktualizacja 20.06.12 --------

Czy próbowali dwukrotnie klikając skrót do aplikacji na pulpicie przy użyciu PInvoke, aby zobaczyć, czy można zobaczyć kontrole, gdy są otwierane w ten sposób? Tu jest link do przykładu tutaj na stackoverflow:

Directing mouse events [DllImport("user32.dll")] click, double click

Inny pomysł: niektóre z kontroli aplikacji Jestem obecnie automatyzujących nie pojawi się w drzewie aż kliknięcie myszą pojawia się na nich. Aby osiągnąć to bez użycia jakichkolwiek współrzędnych na stałe, znajduję coś w drzewie, które jest właśnie (powyżej/poniżej/etc) miejscem, w którym muszę kliknąć, aby sterowanie pojawiło się. Następnie otrzymuję współrzędne myszy dla tego elementu i umieszczam mysz na niewielkim przesunięciu i klikam. Wtedy znajdę moje kontrole w drzewie. Jeśli aplikacja zmieni rozmiar, przeniesie się itp., Nadal będzie działać, ponieważ małe przesunięcie jest nadal poprawne.

+1

Próbowaliśmy tego - nie pomogło. – stukselbax

+0

Dodano kilka dodatkowych rzeczy powyżej ... – chrismead

+0

Przyjdźcie o tym pomyśleć. Możesz zacząć od użycia Process.Start za pomocą skrótu zamiast rzeczywistego exe, aby zobaczyć, czy to pomaga. – chrismead

5

Domyślam się, że po zmianie widoku, programator równorzędny ContentControl powinien zaktualizować swoje dzieci o AutomationPeer.ResetChildrenCache().

AutomationPeer.InvalidatePeer() powinien wywoływać taki sam efekt (oprócz innych efektów ubocznych) i powinien zostać wywołany automatycznie w odpowiedzi na zdarzenie LayoutUpdated. Możesz chcieć sprawdzić, czy zdarzenie LayoutUpdated jest wywoływane podczas zmiany widoku.

+0

Dziękuję bardzo za to - od 24 godzin borykałem się z podobnym problemem, w którym "AutomationElement" nagle zniknął. Po przeczytaniu odpowiedzi sprawdziłem czy dzieci 'AutomationPeer' z danej kontrolki zostały ponownie utworzone podczas' LayoutUpdated' (a nie były) - więc twoja sugestia z wywołaniami do 'ResetChildrenCache()' i 'InvalidatePeer()' zrobiła sztuczka. Dzięki jeszcze raz. +1. –

Powiązane problemy