c# – 当应用程序通过UI自动化测试启动时,ContentControl不可见,但在用户启动应用程序时可以看到ContentControl

我们正在使用棱镜和 WPF来构建应用程序.最近我们开始使用UI自动化(UIA)来测试我们的应用程序.但是当我们运行UIA测试时,会发生一些奇怪的行为.这是简化的shell:
<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,以拒绝对控件的访问.要显示弹出窗口,我们使用下一个方法

//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自动化测试启动它时,ErrorPopup从控件树中消失.我们试图启动这样的应用程序:

System.Diagnostics.Process.Start(pathToExeFile);

我想我们错过了一些东西.但是呢

编辑#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方法搜索按钮以单击,窗口以某种方式缓存其UI自动化状态,并且不更新它.

编辑#2
我们发现,棱镜库的扩展方法IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion,typeof(Views.OurView))有一些奇怪的行为.如果我们停止使用它,这将解决我们的问题.现在我们可以在PopupContentControl中看到ErrorView和任何类型的视图,并且应用程序更新UIA元素树结构.但这不是一个答案 – “停止使用这个功能”!

在MainViewArea中,我们有一个ContentControl,它根据用户操作更新内容,我们只能看到第一个加载的ContentControl到ContentControl.Content属性.这样执行:

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

如果我们更改视图,则在UI自动化树中不会执行任何更新 – 第一个加载的视图将被替换.但是在视觉上我们观察到另一个视图,而WPFInspector显示它正确(它的显示不是UI自动化树),但是Inspect.exe – 不是.

另外我们建议窗口使用某种缓存是错误的 – 在UI Automation客户端中缓存,我们必须明确地打开,但是我们不这样做.

解决方法

对不起,我错过了一些细节,那是答案的关键.我认为这不重要.无论如何.

我们从DevExpress控件库中使用NavBar进行WPF.事实证明,当NavBar存在时,动态创建的视图不会出现在UI自动化树中.当从窗口中删除它时,可以看到所有动态加载的视图. 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);
    }
}

编辑

根据DevExpress answer在他们的支持网站:

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.

解决了这个问题.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...