2012-06-13 16 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。 –

相关问题