2012-06-13 17 views
9

我們使用prism和WPF來構建應用程序。最近我們開始使用UI自動化(UIA)來測試我們的應用程序。但是當我們運行UIA測試時發生了一些奇怪的行爲。這裏的簡化殼:當應用程序通過UI自動化測試啓動時ContentControl不可見,但當應用程序由用戶啓動時它是可見的

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

在我們的應用程序,我們使用圖層(PopupErrorPopup)隱藏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)。但是,當我們使用UI自動化測試啓動它時,從控件的樹中消失。我們試圖啓動這樣的應用程序:

System.Diagnostics.Process.Start(pathToExeFile); 

我認爲我們錯過了一些東西。但是什麼?

編輯#1

正如@chrismead說,我們試圖與UseShellExecute標誌設置爲true運行我們的應用程序,但是這並沒有幫助。但是,如果我們從cmd行啓動應用程序,並且手動單擊該按鈕,PopupErrorPopup在自動化控件樹中可見。

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

我們的一個建議是,當我們使用方法FindAllFindFirst搜索按鈕,點擊,窗口莫名其妙緩存的UI自動化狀態,並且不更新它。

編輯#2 我們已經發現,棱鏡庫IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView))的是擴展方法有一些奇怪的行爲。如果我們停止使用它,這將特別解決我們的問題。現在我們可以看到ErrorView和PopupContentControl中的任何一種視圖,以及應用程序更新UIA元素樹的結構。但這不是答案 - 「請停止使用此功能」!

MainViewArea我們有一個ContentControl,根據用戶的操作,其更新它裏面的內容,我們只能夠看到第一個裝UserControlContentControl.Content財產。這是這樣進行的:

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

如果我們改變了看法,沒有更新將在UI Automation樹進行 - 第一加載視圖將在它來代替。但在視覺上,我們觀察到另一個View,並且WPFInspector正確顯示它(它不顯示UI自動化樹),但Inspect.exe - 不是。

另外我們的建議是,窗口使用某種緩存是錯誤的 - 在UI自動化客戶端緩存,我們必須明確打開,但我們不這樣做。

+1

因此,說一個簡單的雙擊啓動應用程序會導致控件在樹中,但是Process.Start啓動不會呢? – chrismead

+1

是的,它是正確的。但我們嘗試了3種方式從代碼啓動應用程序 - 沒有人讓我們找到正確的解決方案... – stukselbax

+0

您是否嘗試從cmd窗口啓動應用程序?如果可行,那麼使用ProcessStartInfo.UseShellExecute標誌可能會起作用。 – chrismead

回答

7

對不起,我錯過了一些細節,這是答案的關鍵。我認爲這並不重要。無論如何。

我們使用NavBar的DevExpress控件庫WPF。結果是,當NavBar存在時,動態創建的視圖不會出現在UI Automation樹上。當從窗口中移除它時,可以看到所有動態加載的視圖。 NavBar - 對我而言仍然是mistic。

這裏是一個明亮的例子,看看發生了什麼,如果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); 
    } 
} 

編輯

按照DevExpress answer在他們的支持網站:

創建對等體後,監聽自動化事件可能會導致性能問題。我們決定清除自動化事件的調用列表來解決它。在您的特定情況下,您需要禁用清除功能。爲此,請在Window構造函數中將靜態DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled屬性設置爲False。

這解決了這個問題。

+0

謝謝你。我們花了太多時間試圖找出原因。 – Jordan

1

stukselbax,嘗試查找一系列按鍵(TAB和最有可能的ENTER)以單擊可以查看項目的按鈕。發送擊鍵很容易,我可以在這裏添加更多關於這個如果這對你有用。您始終可以在您的應用程序中建立一個對用戶最有意義的Tab鍵順序。

------更新在12年6月20日--------

您是否嘗試過雙擊快捷方式使用的PInvoke桌面上的應用程序,看看你是否能看到控制何時打開?下面是一個例子的鏈接在這裏計算器:

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

另一個想法:一些我目前自動化,直到他們發生了鼠標點擊不樹出現在應用程序的控制。爲了在不使用任何硬編碼座標的情況下完成此任務,我在樹中找到了一些東西,這些東西就在我需要點擊的地方(上方/下方/等)出現。然後我得到該項目的鼠標座標,並將鼠標放在距離那裏的小偏移處,然後單擊。然後我可以在樹中找到我的控件。如果應用程序調整大小,移動等等,這仍然有效,因爲小偏移量仍然有效。

+1

我們試過這個 - 它沒有幫助。 – stukselbax

+0

上面增加了一些額外的東西... – chrismead

+0

來吧,想一想。你可能希望先用一個快捷方式而不是實際的exe來使用Process.Start來查看是否有幫助。 – chrismead

5

我的猜測是,ContentControl的自動化同行應該在視圖更改後使用AutomationPeer.ResetChildrenCache()更新其子級。

AutomationPeer.InvalidatePeer()應該具有相同的效果(除了其他副作用)並且它應該被自動調用以響應LayoutUpdated事件。您可能想要檢查視圖更改時引發的LayoutUpdated事件。

+0

非常感謝您爲此付出了很多努力 - 我一直在奮鬥24小時,其中AutomationElement突然消失。在閱讀你的答案後,我檢查了'LayoutUpdated'期間是否重新創建了給定控件的'AutomationPeer'子元素(並且他們不是) - 因此,你對調用'ResetChildrenCache()'和'InvalidatePeer()'的建議做了招。再次感謝。 +1。 –

相關問題