ContentControl não é visível quando o aplicativo é iniciado por meio do teste de Automação da Interface do Usuário, mas é visível quando o aplicativo é iniciado pelo usuário

Estamos usando o prisma e o WPF para criar aplicativos. Recentemente, começamos a usar a UI Automation (UIA) para testar nosso aplicativo. Mas algum comportamento estranho ocorreu quando corremos o teste UIA. Aqui está o shell simplificado:

          ...          

Em nosso aplicativo, usamos camadas ( Popup e ErrorPopup ) para ocultar MainViewArea , para negar access aos controles. Para mostrar o Popup , usamos o próximo método:

  //In constructor of current ViewModel we store _popupRegion instance to the local variable: _popupRegion = _regionManager.Regions["PopupRegion"]; //--- private readonly Stack _popups = new Stack(); 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; } } 

Similar a isso, mostramos ErrorPopup sobre todos os elementos da nossa aplicação:

  // 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); } } 

Mística …

Quando executamos isso como os usuários fazem (clique duas vezes no ícone do aplicativo), podemos ver os dois controles personalizados (usando o método AutomationElement.FindFirst ou por meio do Visual UI Automation Verify ). Mas quando começamos usando o teste de Automação da Interface do ErrorPopupErrorPopup desaparece da tree dos controles. Estamos tentando iniciar o aplicativo assim:

 System.Diagnostics.Process.Start(pathToExeFile); 

Eu acho que perdemos alguma coisa. Mas o que?

Editar # 1

Como disse @chrismead, tentamos executar nosso aplicativo com o sinalizador UseShellExecute definido como true, mas isso não ajuda. Mas se iniciarmos o aplicativo a partir da linha cmd e ErrorPopup manualmente no botão, o Popup e o ErrorPopup ficarão visíveis na tree de controles de automação.

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

Uma de nossas sugestões é quando usamos o método FindAll ou FindFirst para pesquisar o botão para clicar, a janela de alguma forma FindAll em cache seu estado de Automação da Interface do FindAll e não o atualiza.

Edit # 2 Nós encontramos, esse método de extensão da biblioteca de prismas IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) tem um comportamento estranho. Se paramos de usá-lo, isso resolve nosso problema particularmente. Agora podemos ver ErrorView e qualquer tipo de visão em PopupContentControl , e atualizações de aplicativos na estrutura da tree de elementos UIA. Mas isso não é uma resposta – “Basta parar de usar esse recurso”!

Em MainViewArea , temos um ContentControl , que atualiza o conteúdo dependendo das ações do usuário, e podemos ver apenas o primeiro UserControl carregado nessa propriedade ContentControl.Content . Isso é feito assim:

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

E se alterarmos a exibição, nenhuma atualização será executada na tree de Automação da Interface do Usuário – a primeira exibição carregada estará nela. Mas visualmente observamos outra View , e o WPFInspector a mostra corretamente (seu show não é uma tree de Automação da Interface do Usuário), mas Inspect.exe – não.

Além disso, nossa sugestão de que a janela use algum tipo de armazenamento em cache está errada – o armazenamento em cache no cliente de Automação da Interface do Usuário precisa ser ativado explicitamente, mas não o fazemos.

Me desculpe por ter perdido alguns detalhes, essa foi a chave para a resposta. Eu acho que isso não era importante. De qualquer forma.

Usamos o NavBar da biblioteca de controles do DevExpress para o WPF . O que acontece é que, quando o NavBar está presente, as visualizações criadas dinamicamente não aparecem na tree de Automação da Interface do Usuário. Ao removê-lo da janela, havia a capacidade de ver todas as visualizações carregadas dinamicamente. O que o NavBar – ainda mistic para mim.

Aqui exemplo shiny para ver o que aconteceu, se NavBar está presente ou ausente na janela (DevExpress é necessário).

MainWindow.xaml:

                     

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

Editar

De acordo com a resposta da DevExpress em seu site de suporte:

Depois que um par é criado, ouvir events de automação pode causar problemas de desempenho. Decidimos limpar listas de invocação de events de automação para resolvê-lo. Na sua situação específica, você precisa desativar a compensação. Para fazer isso, defina a propriedade estática DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled como False no construtor Window.

Isso resolve o problema.

Meu palpite é que o ponto de automação do ContentControl deve atualizar seus filhos com o AutomationPeer.ResetChildrenCache() após a exibição ter sido alterada.

AutomationPeer.InvalidatePeer() deve ter o mesmo efeito (além de outros efeitos colaterais) e deve ser chamado automaticamente em resposta ao evento LayoutUpdated . Você pode querer verificar se o evento LayoutUpdated é gerado quando a exibição é alterada.

stukselbax, tente encontrar uma seqüência de pressionamentos de tecla (TABs e um ENTER mais provável) para clicar no botão que permite que você veja os itens. É muito fácil enviar as teclas digitadas e posso adicionar mais informações sobre isso se isso funcionar para você. Você sempre pode estabelecer uma ordem de tabulação em seu aplicativo que faça mais sentido para os usuários.

—— Atualização em 6/20/12 ——–

Você já tentou clicar duas vezes em um atalho para o seu aplicativo na área de trabalho usando o PInvoke para ver se consegue ver os controles quando ele é aberto dessa maneira? Aqui está um link para um exemplo aqui em stackoverflow:

Direcionando events de mouse [DllImport (“user32.dll”)] clique, clique duas vezes

Outra ideia: alguns dos controles no aplicativo que estou automatizando atualmente não aparecem na tree até que um clique do mouse ocorra sobre eles. Para conseguir isso sem usar nenhuma coordenada embutida, acho algo na tree que é apenas (acima / abaixo / etc) o lugar onde eu preciso clicar para fazer o controle aparecer. Eu então obtenho as coordenadas do mouse para aquele item e coloco o mouse em um pequeno deslocamento de lá e clico. Então eu posso encontrar meus controles na tree. Se o aplicativo for redimensionado, movido, etc., isso ainda funcionará, pois o pequeno deslocamento ainda é válido.