2012-06-13 20 views
9

Chúng tôi đang sử dụng lăng kính và WPF để xây dựng ứng dụng. Gần đây, chúng tôi đã bắt đầu sử dụng Giao diện người dùng Tự động hóa (UIA) để thử nghiệm ứng dụng của chúng tôi. Nhưng một số hành vi lạ xảy ra khi chúng tôi chạy thử nghiệm UIA. vỏ Dưới đây là đơn giản:ContentControl không hiển thị khi ứng dụng bắt đầu qua kiểm tra Tự động hóa giao diện người dùng, nhưng hiển thị khi ứng dụng bắt đầu bằng người dùng

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

Trong ứng dụng của chúng tôi, chúng tôi sử dụng các lớp (PopupErrorPopup) để ẩn MainViewArea, để từ chối quyền truy cập vào các điều khiển. Để hiển thị Popup, chúng tôi sử dụng phương pháp tiếp theo:

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

Tương tự như này, chúng tôi hiển thị ErrorPopup khắp các yếu tố của ứng dụng của chúng tôi:

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

Khi chúng tôi chạy khi người dùng làm điều đó (nhấp đúp vào biểu tượng ứng dụng), chúng tôi có thể thấy cả hai điều khiển tùy chỉnh (sử dụng phương thức AutomationElement.FindFirst hoặc thông qua Visual UI Automation Verify). Nhưng khi chúng tôi bắt đầu nó bằng cách sử dụng kiểm tra tự động hóa giao diện người dùng - ErrorPopup disapears từ cây điều khiển. Chúng tôi đang cố gắng bắt đầu ứng dụng như thế này:

System.Diagnostics.Process.Start(pathToExeFile); 

Tôi nghĩ rằng chúng tôi đã bỏ lỡ điều gì đó. Nhưng cái gì?

Chỉnh sửa # 1

Như @chrismead nói, chúng tôi đã cố gắng để chạy ứng dụng của chúng tôi với UseShellExecute cờ thiết lập là true, nhưng điều này không giúp đỡ. Nhưng nếu chúng ta bắt đầu ứng dụng từ cmd dòng và nhấp vào nút thủ công, PopupErrorPopup hiển thị trong cây điều khiển tự động.

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

Một trong những gợi ý của chúng tôi là khi chúng ta sử dụng phương pháp FindAll hoặc FindFirst để tìm kiếm trên nút bấm, cửa sổ bằng cách nào đó được lưu trữ nhà nước UI Automation của nó, và không cập nhật nó.

Chỉnh sửa # 2 Chúng tôi đã tìm thấy, phương pháp mở rộng của thư viện lăng kính IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) có một số hành vi lạ. Nếu chúng ta ngừng sử dụng nó, điều này giải quyết vấn đề của chúng ta. Bây giờ chúng ta có thể thấy ErrorView và bất kỳ dạng xem nào trong PopupContentControl và ứng dụng cập nhật cấu trúc cây phần tử UIA. Nhưng đây không phải là câu trả lời - "Chỉ cần ngừng sử dụng tính năng này"!

Trong MainViewArea chúng tôi có một ContentControl, cập nhật nội dung nó tùy thuộc vào hành động của người dùng, và chúng tôi có thể chỉ nhìn thấy đầu tiên nạp UserControl đó ContentControl.Content tài sản. Điều này được thực hiện như sau:

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

Và nếu chúng ta thay đổi chế độ xem, sẽ không có cập nhật nào được thực hiện trong cây Tự động hóa giao diện người dùng. Nhưng trực quan, chúng tôi quan sát một số khác ViewWPFInspector hiển thị chính xác (chương trình của nó không phải là cây Tự động hóa giao diện người dùng), nhưng Inspect.exe - không.Ngoài ra gợi ý của chúng tôi rằng cửa sổ sử dụng một số loại bộ nhớ đệm là sai - bộ nhớ đệm trong khách hàng tự động hóa giao diện người dùng, chúng tôi phải bật một cách rõ ràng, nhưng chúng tôi không làm điều đó.

+1

Vì vậy, có chính xác khi nói rằng việc khởi chạy nhấp đúp đơn giản của kết quả ứng dụng trong điều khiển nằm trong cây, nhưng khởi chạy Process.Start không? – chrismead

+1

Vâng, đúng. Nhưng chúng tôi đã thử 3 cách để bắt đầu ứng dụng từ mã - không ai đưa chúng ta đến giải pháp đúng ... – stukselbax

+0

Bạn đã thử khởi chạy ứng dụng từ cửa sổ cmd chưa? Nếu nó hoạt động thì sử dụng cờ ProcessStartInfo.UseShellExecute có thể hoạt động. – chrismead

Trả lời

7

Tôi xin lỗi vì tôi đã bỏ lỡ một số chi tiết, đó là chìa khóa cho câu trả lời. Tôi nghĩ rằng đó không phải là điều quan trọng. Dù sao.

Chúng tôi sử dụng NavBar từ DevExpress thư viện điều khiển cho WPF. Điều gì xảy ra, là khi NavBar có mặt, các chế độ xem được tạo động sẽ không xuất hiện trên cây Tự động hóa giao diện người dùng. Khi xóa nó khỏi cửa sổ, có khả năng xem tất cả các chế độ xem được tải động. Những gì hiện NavBar - vẫn còn mistic cho tôi.

Đây là ví dụ sáng để xem điều gì đã xảy ra, nếu NavBar có mặt hoặc vắng mặt trên Cửa sổ (yêu cầu 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); 
    } 
} 

Sửa

Theo DevExpress answer trên trang web hỗ trợ của họ:

Sau khi một peer được tạo ra, việc nghe các sự kiện tự động hóa có thể gây ra các vấn đề về hiệu suất. Chúng tôi đã quyết định xóa danh sách yêu cầu của các sự kiện tự động hóa để giải quyết nó.Trong tình huống cụ thể của bạn, bạn cần phải tắt thanh toán bù trừ. Để làm điều đó, hãy đặt thuộc tính DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled tĩnh thành False trong trình tạo cửa sổ.

Việc này giải quyết được sự cố.

+0

Cảm ơn bạn vì điều này. Chúng tôi đã thổi quá nhiều thời gian để theo dõi lý do cho việc này. – Jordan

1

stukselbax, cố gắng tìm một chuỗi các tổ hợp phím (TAB và ENTER rất có thể) để nhấp vào nút cho phép bạn xem các mục. nó là khá dễ dàng để gửi tổ hợp phím và tôi có thể thêm nhiều hơn ở đây về điều đó nếu điều đó làm việc cho bạn. bạn luôn có thể thiết lập một thứ tự tab trong ứng dụng của bạn có ý nghĩa nhất đối với người dùng.

------ Cập nhật vào 6/20/12 --------

Các bạn đã thử click đúp vào một phím tắt để ứng dụng của bạn trên máy tính để bàn sử dụng PInvoke để xem nếu bạn có thể nhìn thấy các điều khiển khi nó được mở theo cách đó? Đây là một liên kết đến một ví dụ ở đây trên stackoverflow:

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

ý tưởng khác: một số các điều khiển trên ứng dụng Tôi hiện đang tự động không hiển thị trong cây cho đến khi một click chuột xảy ra trên chúng. Để thực hiện điều này mà không cần sử dụng bất kỳ tọa độ cứng nào, tôi tìm thấy thứ gì đó trong cây chỉ là (ở trên/dưới/etc) nơi tôi cần nhấn để điều khiển xuất hiện. Sau đó tôi nhận tọa độ chuột cho mục đó và đặt chuột ở một khoảng nhỏ từ đó và nhấp vào. Sau đó, tôi có thể tìm thấy điều khiển của tôi trong cây. Nếu ứng dụng được thay đổi kích thước, di chuyển xung quanh, v.v. điều này sẽ vẫn hoạt động vì khoản bù trừ nhỏ vẫn hợp lệ.

+1

Chúng tôi đã thử điều này - nó không được giúp đỡ. – stukselbax

+0

Đã thêm một số nội dung bổ sung ở trên ... – chrismead

+0

Hãy nghĩ về điều đó. Bạn có thể muốn bắt đầu bằng cách sử dụng Process.Start với một phím tắt thay vì exe thực tế để xem điều đó có hữu ích hay không. – chrismead

5

Tôi đoán là máy ngang hàng tự động của ContentControl nên cập nhật con của nó bằng AutomationPeer.ResetChildrenCache() sau khi chế độ xem đã được thay đổi.

AutomationPeer.InvalidatePeer() sẽ có tác dụng tương tự (ngoài tác dụng phụ khác) và được cho là được gọi tự động theo sự kiện LayoutUpdated. Bạn có thể muốn kiểm tra xem sự kiện LayoutUpdated có được nâng lên khi chế độ xem thay đổi hay không.

+0

Cảm ơn bạn rất nhiều vì điều này - Tôi đã phải vật lộn trong 24 giờ với một loại vấn đề tương tự khi 'AutomationElement's đột nhiên biến mất. Sau khi đọc câu trả lời của bạn, tôi đã kiểm tra nếu con 'AutomationPeer' của một điều khiển đã cho được tạo lại trong' LayoutUpdated' (và chúng không) - vì vậy gợi ý của bạn với các lời gọi tới 'ResetChildrenCache()' và 'InvalidatePeer()' đã làm lừa. Cảm ơn một lần nữa. +1. –

Các vấn đề liên quan