2012-06-13 11 views
9

Estamos utilizando el prisma y WPF para crear la aplicación. Recientemente comenzamos a usar la UI Automation (UIA) para probar nuestra aplicación. Pero algún comportamiento extraño ocurrió cuando ejecutamos la prueba UIA. Aquí está la cáscara simplificado:ContentControl no está visible cuando la aplicación se inicia a través de la prueba de automatización de UI, pero es visible cuando la aplicación se inicia por el usuario

<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> 

En nuestra aplicación, usamos capas (Popup y ErrorPopup) para ocultar MainViewArea, para negar el acceso a los controles. Para mostrar Popup, utilizamos el método siguiente:

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

Similar a esto, mostramos ErrorPopup sobre todos los elementos de nuestra aplicación:

// 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ístics ...

Cuando corremos como lo hacen los usuarios (haga doble clic en el ícono de la aplicación), podemos ver ambos controles personalizados (usando el método AutomationElement.FindFirst, o a través del Visual UI Automation Verify). Pero cuando lo comenzamos usando la prueba de automatización UI - ErrorPopup desaparece del árbol de los controles. Estamos tratando de iniciar la aplicación de esta manera:

System.Diagnostics.Process.Start(pathToExeFile); 

Creo que nos hemos perdido algo. ¿Pero que?

Edición # 1

Como dijo @chrismead, tratamos de ejecutar nuestra aplicación con UseShellExecute indicador se define como true, pero esto no soluciona el problema. Pero si iniciamos la aplicación desde la línea cmd y hacemos clic manualmente en el botón, Popup y ErrorPopup están visibles en el árbol de controles de automatización.

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

Una de nuestra sugerencia es cuando usamos el método FindAll o FindFirst para buscar el botón para hacer clic, la ventana de algún modo en caché su estado de UI Automation, y no lo actualiza.

Edición # 2 Hemos de encontrar, que el método de extensión de la biblioteca prisma IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) tienen un comportamiento extraño. Si dejamos de usarlo, esto resolverá nuestro problema en particular. Ahora podemos ver ErrorView y cualquier tipo de vista en PopupContentControl, y la aplicación actualiza la estructura del árbol de los elementos UIA. Pero esta no es una respuesta: "¡Solo deja de usar esta característica"!

En MainViewArea tenemos una ContentControl, que actualiza su contenido en función de las acciones del usuario, y que son capaces de ver sólo la primera carga UserControl a que ContentControl.Content propiedad. Esto se realiza así:

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

Y si cambiamos el punto de vista, no hay actualizaciones se realizan en el árbol de UI Automation - la primera vista cargada estará en su lugar. Pero visualmente observamos otro View, y WPFInspector lo muestra correctamente (su show no es un árbol de Automatización de UI), pero Inspect.exe - no.

también nuestra sugerencia de que la ventana use algún tipo de almacenamiento en caché está mal - el almacenamiento en caché en el cliente de UI Automation tenemos que activar de forma explícita, pero no lo hacemos.

+1

lo tanto, es correcto decir que un simple doble clic lanzamiento de los resultados de la aplicación en el control está en el árbol, pero el lanzamiento de un Process.Start no lo hace? – chrismead

+1

Sí, es correcto. Pero probamos 3 maneras de iniciar la aplicación desde el código: nadie nos lleva a la solución correcta ... – stukselbax

+0

¿Has probado a ejecutar la aplicación desde una ventana de cmd? Si eso funciona, entonces podría funcionar el uso del indicador ProcessStartInfo.UseShellExecute. – chrismead

Respuesta

7

Siento que me he perdido algún detalle, que era la clave de la respuesta. Creo que no era algo importante. De todas formas.

Utilizamos NavBar de DevExpress biblioteca controles de WPF . Lo que sucede es que cuando NavBar está presente, las vistas creadas dinámicamente no aparecen en el árbol de automatización de UI. Al eliminarlo de la ventana, se pudo ver todas las vistas cargadas dinámicamente. ¿Qué hace el NavBar? Todavía estoy loco por mí.

ejemplo Aquí brillante como para ver lo que ha pasado, si barra de navegación está presente o ausente en la ventana (se requiere 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); 
    } 
} 

Editar

De acuerdo con la DevExpress answer en su sitio de soporte:

Después de crear un par, escuchar eventos de automatización puede causar problemas de rendimiento. Hemos decidido borrar las listas de invocación de eventos de automatización para resolverlo.En su situación específica, necesita desactivar el borrado. Para hacerlo, establezca la propiedad estática DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled en False en el constructor de Windows.

Esto resuelve el problema.

+0

Gracias por esto. Pasamos mucho tiempo intentando rastrear el motivo de esto. – Jordan

1

stukselbax, tratar de encontrar una secuencia de pulsaciones de teclas (TABS, y un ENTER más probable) hacer clic en el botón que le permite ver los objetos. es bastante fácil enviar pulsaciones de teclas y puedo agregar más sobre esto si eso funciona para usted. siempre puede establecer un orden de tabulación en su aplicación que tenga más sentido para los usuarios.

------ -------- 20/06/12 Actualización sobre

Ha intentado haciendo doble clic en un acceso directo a la aplicación en el escritorio usando PInvoke para ver si se puede ver los controles cuando se abre de esa manera? Aquí hay un enlace a un ejemplo aquí en stackoverflow:

Directing mouse events [DllImport("user32.dll")] click, double click

Otra idea: algunos de los controles de la aplicación Actualmente estoy automatizando no aparecen en el árbol hasta que se produce un clic del ratón sobre ellos. Para lograr esto sin utilizar ninguna de las coordenadas codificadas, encuentro algo en el árbol que es justo (arriba/abajo/etc.) el lugar donde necesito hacer clic para que aparezca el control. Luego obtengo las coordenadas del mouse para ese elemento y coloco el mouse en un pequeño desplazamiento desde allí y hago clic. Entonces puedo encontrar mis controles en el árbol. Si la aplicación cambia de tamaño, se mueve, etc., esto seguirá funcionando, ya que el pequeño desplazamiento sigue siendo válido.

+1

Hemos intentado esto, no se ayudó. – stukselbax

+0

Agregó algunas cosas adicionales arriba ... – chrismead

+0

Ahora que lo pienso. Es posible que desee comenzar utilizando Process.Start con un acceso directo en lugar del propio ejecutable para ver si eso ayuda. – chrismead

5

Supongo que el par de automatización ContentControl debe actualizar sus hijos con AutomationPeer.ResetChildrenCache() después de que se haya cambiado la vista.

AutomationPeer.InvalidatePeer() debe tener el mismo efecto (además de otros efectos secundarios) y se supone que debe invocarse automáticamente en respuesta al evento LayoutUpdated. Es posible que desee comprobar que el evento LayoutUpdated se genera cuando cambia la vista.

+0

Gracias tan mucho para esto - He estado luchando durante 24 horas con una especie de problema similar donde 'AutomationElement's desapareció de repente. Después de leer su respuesta he comprobado si se vuelven a crear los niños 'AutomationPeer' de un control determinado durante' LayoutUpdated' (y no fueron) - por lo que su sugerencia con llamadas a 'ResetChildrenCache()' y 'InvalidatePeer()' Hice la truco. Gracias de nuevo. +1. –

Cuestiones relacionadas