2012-06-13 5 views
9

프리즘과 WPF를 사용하여 응용 프로그램을 작성합니다. 최근 우리는 UI 자동화 (UIA)를 사용하여 앱을 테스트하기 시작했습니다. 그러나 UIA 테스트를 실행할 때 이상한 행동이 발생했습니다. 여기에 단순화 된 것 쉘 : 우리의 응용 프로그램에서ContentControl은 UI Automation 테스트를 통해 응용 프로그램이 시작될 때 보이지 않지만 응용 프로그램이 사용자에 의해 시작될 때 표시됩니다

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

, 우리는 컨트롤에 대한 액세스를 거부, MainViewArea을 숨길 층 (PopupErrorPopup)를 사용합니다. 우리가 실행하면 ...

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

을 : 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을 보여 사용자가 (앱 아이콘을 두 번 클릭하면) 사용자 정의 컨트롤 (AutomationElement.FindFirst 메소드 또는 Visual UI Automation Verify을 사용)을 볼 수 있습니다. 그러나 UI 자동화 테스트를 사용하여 시작할 때 - ErrorPopup은 컨트롤 트리에서 사라집니다. 다음과 같이 애플리케이션을 시작하려고합니다 :

System.Diagnostics.Process.Start(pathToExeFile); 

뭔가를 놓친 것 같습니다. 근데 뭐? @chrismead 말했듯이

편집 # 1

, 우리는 true로 설정 UseShellExecute 플래그와 함께 우리의 응용 프로그램을 실행하려고했으나이 도움이되지 않습니다. 그러나 cmd 행에서 앱을 시작하고 버튼을 수동으로 클릭하면 PopupErrorPopup이 자동화 컨트롤 트리에 표시됩니다. 우리가 클릭 버튼을 검색하는 방법 FindAll 또는 FindFirst를 사용할 때 우리의 제안의

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

하나는, 윈도우는 어떻게 든 자사의 UI 자동화 상태를 캐시하고 업데이트하지 않습니다.

우리가 발견 한 편집 # 2 는, 프리즘 라이브러리 IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView))의 확장 방법은 몇 가지 이상한 행동을합니다. 우리가 그것을 사용을 중단하면, 이것은 우리의 문제를 해결합니다. 이제 우리는 PopupContentControl에서 ErrorView 및 모든 종류의보기를 볼 수 있었고 응용 프로그램은 UIA 요소 트리 구조를 업데이트했습니다. 그러나 이것은 대답이 아닙니다. "그냥이 기능 사용을 중지하십시오!" MainViewArea에서

우리는 사용자의 행동에 따라 컨텐츠를 업데이트하는 ContentControl을 가지고 있고, 우리는 첫 번째가 ContentControl.Content 속성에 UserControl로드 만 볼 수 있습니다.

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

을 우리가보기를 변경하는 경우에는 업데이트는 UI 자동화 트리에 수행되지 않습니다 - 최초의로드 뷰는 대신에있을 것입니다 : 이것은 다음과 같이 수행한다. 그러나 시각적으로 우리는 또 다른 View을 관찰하고 WPFInspector은 제대로 표시합니다 (UI 자동화 트리가 아닌). 그러나 Inspect.exe는 그렇지 않습니다.

또한 윈도우가 일종의 캐싱을 사용한다는 제안은 잘못되었습니다. 우리는 명시 적으로 켜야하는 UI 자동화 클라이언트에서 캐싱을하지만 캐싱하지는 않습니다.

+1

그렇다면 응용 프로그램을 두 번 클릭하면 컨트롤이 트리에 있지만 Process.Start는 실행되지 않습니다. – chrismead

+1

예, 맞습니다. 그러나 우리는 코드에서 앱을 시작하는 3 가지 방법을 시도했습니다. 아무도 올바른 솔루션을 얻지 못했습니다 ... – stukselbax

+0

cmd 창에서 앱을 시작하려 했습니까? 그런 다음 ProcessStartInfo.UseShellExecute 플래그를 사용하면 작동합니다. – chrismead

답변

7

일부 세부 사항을 놓쳐서 죄송합니다. 답변의 열쇠였습니다. 나는 그것이 중요한 것이 아니라고 생각한다. 어쨌든.

우리는 WPF에 대한 DevExpress의 제어 라이브러리에서 NavBar을 사용했다. 밝혀지는 것은 NavBar이 존재할 때 동적으로 생성 된 뷰가 UI 자동화 트리에 나타나지 않는다는 것입니다. 창에서 제거 할 때 동적으로로드 된 모든 뷰를 볼 수있었습니다. 무엇이 NavBar - 여전히 나를 위해 mistic.

NavBar가 존재하거나 존재하지 않으면 DevExpress가 필요합니다.

MainWindow.xaml : 자신의 지원 사이트에 DevExpress answer에 따르면

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

편집

:

피어가 작성된 후 자동화 이벤트를 수신하면 성능 문제가 발생할 수 있습니다. 우리는이를 해결하기 위해 자동화 이벤트의 호출 목록을 정리하기로 결정했습니다.특정 상황에서는 지우기를 비활성화해야합니다. 이를 수행하려면 Window 생성자에서 정적 DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled 속성을 False로 설정하십시오.

이렇게하면 문제가 해결됩니다.

+0

고맙습니다. 우리는 그 이유를 추적하려고 너무 많은 시간을 허비했습니다. – Jordan

1

stukselbax 인 경우, 항목을 볼 수있는 단추를 클릭하여 키 입력 순서 (TAB 및 Enter 키를 누르십시오)를 찾으십시오. 그것은 키 스트로크를 보내는 것은 꽤 쉽습니다. 그리고 그것이 당신을 위해 작동한다면 나는 그것에 대해 더 많은 것을 여기에 더할 수 있습니다. 사용자가 가장 잘 이해할 수있는 탭 순서를 응용 프로그램에 설정할 수 있습니다.

------에 업데이트 6/20/12 --------

당신이 이중 당신이 볼 수 있는지의 PInvoke를 사용하여 바탕 화면에 앱 바로 가기를 클릭 시도 되세요 그런 식으로 열 때 컨트롤?

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

또 다른 아이디어 : 여기에 유래에 예를 들어 여기에 대한 링크는 내가 현재 마우스 클릭이 그들에 발생할 때까지 트리에 표시되지 않는 자동화하고 응용 프로그램의 컨트롤 중 일부는. 하드 코드 된 좌표를 사용하지 않고이를 수행하기 위해 컨트롤을 표시하기 위해 클릭해야하는 곳 (위/아래/등) 인 트리에서 무언가를 찾습니다. 그런 다음 해당 항목에 대한 마우스 좌표를 가져 와서 거기에서 작은 오프셋으로 마우스를 놓고 클릭하십시오. 그런 다음 나무에서 컨트롤을 찾을 수 있습니다. 응용 프로그램의 크기가 조정되거나 이동 된 경우 등 작은 오프셋이 여전히 유효하기 때문에 여전히 작동합니다.

+1

우리는 이것을 시도했습니다 - 도움이되지 않았습니다. – stukselbax

+0

위의 몇 가지 추가 사항을 추가했습니다. – chrismead

+0

생각해보십시오. 실제 exe 대신 바로 가기를 사용하여 Process.Start를 사용하여 시작하여 도움이되는지 확인할 수 있습니다. – chrismead

5

내 생각에 ContentControl의 자동화 피어는보기가 변경된 후에 AutomationPeer.ResetChildrenCache()으로 자식을 업데이트해야합니다.

AutomationPeer.InvalidatePeer()은 (다른 부작용 이외에) 동일한 효과가 있어야하며 LayoutUpdated 이벤트에 대한 응답으로 자동으로 호출되어야합니다. 뷰가 변경 될 때 LayoutUpdated 이벤트가 발생하는지 확인할 수 있습니다.

+0

sooo 이것에 대해 많이 고맙습니다. 저는 24 시간 동안 'AutomationElement'가 갑자기 사라지는 비슷한 문제로 고생하고 있습니다. 당신의 대답을 읽은 후에 주어진 컨트롤의'AutomationPeer' 자식들이'LayoutUpdated' 동안 (그리고 그것들이 아닌) 다시 만들어 졌는지 확인했습니다. 그래서'ResetChildrenCache()'와'InvalidatePeer()'에 대한 여러분의 제안은 장난. 다시 한번 감사드립니다. +1. –

관련 문제