<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.
这解决了这个问题.