2010-05-05 5 views
4

J'utilise Microsoft UI Automation (à savoir AutomationElement) pour exécuter des tests d'acceptation automatisés contre ma demande. Cela s'est bien passé, mais j'ai rencontré une situation qui ne semble pas exposée au cadre d'automatisation.Comment utiliser UI Automation sur un objet WPF ItemsControl qui regroupe des éléments?

J'ai un ItemsControl (bien que je puisse utiliser l'un de ses contrôles dérivés, par exemple ListBox) et j'utilise CollectionViewSource pour regrouper des éléments. Voici une fenêtre complète pour démontrer:

<Window x:Class="GroupAutomation.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Orchestra"> 
    <Window.Resources> 

     <!-- Take some simple data --> 
     <XmlDataProvider x:Key="SampleData" XPath="Orchestra/Instrument"> 
      <x:XData> 
       <Orchestra xmlns=""> 
        <Instrument Name="Flute" Category="Woodwind" /> 
        <Instrument Name="Trombone" Category="Brass" /> 
        <Instrument Name="French horn" Category="Brass" /> 
       </Orchestra> 
      </x:XData> 
     </XmlDataProvider> 

     <!-- Add grouping --> 
     <CollectionViewSource Source="{Binding Source={StaticResource SampleData}}" x:Key="GroupedView"> 
      <CollectionViewSource.GroupDescriptions> 
       <PropertyGroupDescription PropertyName="@Category" /> 
      </CollectionViewSource.GroupDescriptions> 
     </CollectionViewSource> 
    </Window.Resources> 

    <!-- Show it in an ItemsControl --> 
    <ItemsControl ItemsSource="{Binding Source={StaticResource GroupedView}}" HorizontalAlignment="Left" Margin="4"> 
     <ItemsControl.GroupStyle> 
      <GroupStyle> 
       <GroupStyle.HeaderTemplate> 
        <DataTemplate> 
         <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" /> 
        </DataTemplate> 
       </GroupStyle.HeaderTemplate> 
      </GroupStyle> 
     </ItemsControl.GroupStyle> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <Border Padding="4" Margin="4" Background="#FFDEDEDE"> 
        <StackPanel> 
         <Label Content="{Binding [email protected]}" /> 
         <Button Content="Play" /> 
        </StackPanel> 
       </Border> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</Window> 

Ce produit une fenêtre contenant les éléments regroupés dans leurs catégories, et chaque élément a un bouton que je voudrais cliquer avec UI Automation:

Screenshot of window with a list http://pics.brizzly.com/thumb_lg_2C45.jpg

Cependant, si je regarde dans UISpy.exe (ou naviguer avec le AutomationElement) Je ne vois les groupes (même dans la vue Raw):

UISpy http://pics.brizzly.com/thumb_lg_2C47.jpg

Comme vous pouvez le voir, les groupes sont là, mais ils ne contiennent pas d'éléments, donc il n'y a nulle part où chercher les boutons. J'ai essayé ceci dans WPF 3.5 SP1 et WPF 4.0 et obtiens le même résultat.

Est-il possible d'utiliser UI Automation sur les éléments qui sont regroupés, et si oui, comment?

+0

Utilisez directement les éléments de contrôle des articles comme indiqué ici. [link] (http://abusanad.net/2016/12/19/wpf-itemscontrol-with-teststack-white/) – user1912383

Répondre

1

Quels outils utilisez-vous pour écrire les scripts automatisés? J'aurais pensé qu'il y aurait une option pour percer dans les arbres logiques/visuels de WPF plutôt que de se fier à l'arbre Win32 (tel qu'apparu UISpy).

Si vous avez un oeil à la même application à l'aide Snoop, vous verrez les pleins arbres visuels et logiques.

+0

J'écris des tests d'acceptation en utilisant MbUnit (mais pourrait être NUnit, xUnit.NET ou autre) et piloter l'application en utilisant le framework UI Automation. Cela dissocie les tests de l'implémentation (dans une certaine mesure) en traitant les éléments de l'interface utilisateur que l'utilisateur peut voir et/ou interagir avec plutôt que de regarder directement les arborescences WPF. Je fais cela depuis environ un an et c'est le premier cas que j'ai trouvé qui ne fonctionne pas seulement, donc j'espère combler le fossé plutôt que de changer de cadre à ce stade. – GraemeF

2

Je ne suis pas sûr à 100% sur les boutons, mais TextBlock contrôles qui sont à l'intérieur DataTemplate s Ne pas se mettre dans l'arborescence UI Automation. Apparemment c'est une optimisation pour éviter 1000 de blocs de texte inutiles.

Vous pouvez travailler autour d'elle par le sous-classement TextBlock. Voici le mien:

public class AutomatableTextBlock : TextBlock 
{ 
    protected override AutomationPeer OnCreateAutomationPeer() 
    { 
     return new AutomatableTextBlockAutomationPeer(this); 
    } 

    class AutomatableTextBlockAutomationPeer : TextBlockAutomationPeer 
    { 
     public AutomatableTextBlockAutomationPeer(TextBlock owner) 
      : base(owner) 
     { } 

     protected override bool IsControlElementCore() 
     { return true; } 
    } 
} 

Note: UI Automation également ne pas exposer d'autres contrôles comme Canvas, Panel, vous pouvez les obtenir pour montrer avec une sous-classe similaire.

En disant cela, je ne sais pas pourquoi le Button ne comparaît pas .... Hrmmm

+0

Les éléments dont j'ai besoin s'affichent très bien si le regroupement est désactivé, donc je ne pense pas que votre réponse m'aide. J'ai utilisé un bouton dans l'exemple pour plus de clarté car ils apparaissent toujours (sauf dans ce cas!). – GraemeF

2

J'ai fini par résoudre dans mon application en utilisant TreeWalker.RawViewWalker pour naviguer manuellement l'arbre après avoir utilisé AutomationElement.FindFirst pour trouver le gabarit. FindFirst semble exclure de manière fiable toutes les informations que vous souhaitez lors de l'automatisation de l'application de quelqu'un d'autre. RawViewWalker semble fonctionner lorsque les éléments apparaissent dans "Inspecter les objets", mais pas dans UISpy ou votre application.

+0

Ça a marché! Fait intéressant, UISpy n'a pas pu trouver de nœud enfant sur un ordinateur mais en a trouvé un autre. Les deux ordinateurs ont les mêmes spécifications et le même logiciel installé. Quoi qu'il en soit, l'astuce avec TreeWalker a fonctionné pour les deux. – SlavaGu

3

J'ai rencontré ce problème et j'ai réussi à le résoudre en implémentant un 'GenericAutomationPeer' de http://www.colinsalmcorner.com/post/genericautomationpeer--helping-the-coded-ui-framework-find-your-custom-controls et en ajoutant un cas spécial pour GroupItem s.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Automation; 
using System.Windows.Automation.Peers; 
using System.Windows.Media; 
using System.Xml; 

namespace ClassLibrary1 
{ 
    public class MyItemsControl : ItemsControl 
    { 
     protected override AutomationPeer OnCreateAutomationPeer() 
     { 
      return new GenericAutomationPeer(this); 
     } 
    } 

    public class GenericAutomationPeer : UIElementAutomationPeer 
    { 
     public GenericAutomationPeer(UIElement owner) : base(owner) 
     { 
     } 

     protected override List<AutomationPeer> GetChildrenCore() 
     { 
      var list = base.GetChildrenCore(); 
      list.AddRange(GetChildPeers(Owner)); 
      return list; 
     } 

     private List<AutomationPeer> GetChildPeers(UIElement element) 
     { 
      var list = new List<AutomationPeer>(); 
      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) 
      { 
       var child = VisualTreeHelper.GetChild(element, i) as UIElement; 
       if (child != null) 
       { 
        AutomationPeer childPeer; 
        if (child is GroupItem) 
        { 
         childPeer = new GenericAutomationPeer(child); 
        } 
        else 
        { 
         childPeer = UIElementAutomationPeer.CreatePeerForElement(child); 
        } 
        if (childPeer != null) 
        { 
         list.Add(childPeer); 
        } 
        else 
        { 
         list.AddRange(GetChildPeers(child)); 
        } 
       } 
      } 
      return list; 
     } 
    } 

} 

J'espère que cela aide tous ceux qui cherchent toujours une réponse!

+0

Ce n'est pas idéal mais en considérant un effort de fixer AutomationPeer pour ItemsControls groupés c'est une solution de contournement très efficace. Merci! –

Questions connexes