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

标签 c# wpf xaml devexpress ui-automation

我们正在使用棱镜和 WPF 来构建应用程序。最近我们开始使用 UI 自动化 (UIA) 来测试我们的应用程序。但是当我们运行 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>

在我们的应用程序中,我们使用层(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);
        }
    }

迷信学...

当我们像用户一样运行它时(双击应用程序图标),我们可以看到两个自定义控件(使用 AutomationElement.FindFirst 方法,或通过 Visual UI Automation Verify )。但是当我们使用 UI 自动化测试启动它时 - ErrorPopup 从控件树中消失了。我们试图像这样启动应用程序:

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)) 的扩展方法有一些奇怪的行为。如果我们停止使用它,这将特别解决我们的问题。现在我们可以在 PopupContentControl 中看到 ErrorView 和任何类型的 View ,并且应用程序更新 UIA 元素树结构。但这不是答案 - “停止使用此功能”!

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

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

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

另外,我们关于窗口使用某种缓存的建议是错误的 - 在 UI Automation 客户端中缓存我们必须显式打开,但我们没有这样做。

最佳答案

很抱歉,我错过了一些细节,这是答案的关键。我认为这并不重要。无论如何。

我们使用 DevExpress 控件库中的 NavBar 用于 WPF。结果是,当 NavBar 存在时,动态创建的 View 不会出现在 UI 自动化树上。从窗口中删除它时,可以看到所有动态加载的 View 。 NavBar 的作用 - 对我来说仍然很模糊。

如果 NavBar 在窗口上存在或不存在(需要 DevExpress),这里是一个很好的例子,看看发生了什么。

主窗口.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>

主窗口.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.

这解决了问题。

关于c# - ContentControl 在通过 UI 自动化测试启动应用程序时不可见,但在用户启动应用程序时可见,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11014771/

相关文章:

c# - InvalidCastException,COM 组件上的 QueryInterface 调用失败

c# - 优化 Entity Framework

c# - 在 OpenXml 复选框 word2013 中设置值

c# - 根据 datatrigger 属性更改绑定(bind)

c# - 等效于 WPF .NET 5、.NET 6 或 .Net Core 中的 UserSettings/ApplicationSettings

c# - ItemsControl 忽略资源中的 DataTemplates

c# - UWP 如何绑定(bind)两个值?

wpf - 仅在设计时使用 MultiBindingConverter 获取 XamlObjectWriterException (Visual Studio 2015)

c# - 使用 Facebook API 编辑帖子返回 OAuthException #100

c# - MVC ValidationMessageFor 无法正常工作