Custom Provider for Datagrid Control

Jul 10, 2009 at 2:21 PM

Does anyone have an example of how to write a custom provider for a datagrid control or any other control? I am having trouble getting White or UISpy to see the elements inside of the datagrid I am testing. I know a custom provider can provide a means of helping White (or UIAutomation) see the contents of the control. 

Jul 10, 2009 at 2:43 PM

Hi again

 

here the way we use it for datagrids :) in an VisualStudio integrated tool.

 

ListView view = window.Get<ListView>(SearchCriteria.ByControlType(ControlType.DataGrid).AndByAutomationElement("ctlListView"));

 

but if uispy don't see it,, it might be possible that the application you wan't to check don't implement uiautomation parts you need to find it)

 

if you have the above element you can easy use

 

ListViewRows rows = view.Rows;

rows[0].Cells["Column Name"];

 

to have access to the single elements

Jul 10, 2009 at 3:36 PM
Edited Jul 10, 2009 at 3:38 PM

Thanks for the suggestion. I wish that my problem was similar, but my datagrid is listed as a pane control.  I was hoping to find out how to write a custom provider so my automation tests could have another means for accessing the contents of the grid.

Jul 10, 2009 at 3:59 PM

ok

that means you see a grid but it is a pane.

This is similar to the property dialog in VisualStudio. And we haven't found any way to use white here. We use AutomationElement for that part. :(

 

Throndorin

Jul 10, 2009 at 4:16 PM

So you found a work around using automation element for similar situation?  If you don't mind sharing or if you have a sample I would really appreciate the help.

Jul 16, 2009 at 11:22 AM

Hi

 

sorry for late reply, I'm on vacation.

 

next week I will give you an example

Throndorin

Jul 16, 2009 at 5:45 PM

Thanks. The assistance is appreciated.

Jul 21, 2009 at 8:04 AM
Edited Jul 21, 2009 at 8:10 AM

 

/// <summary>
  /// Type of the Property Window
  /// </summary>
  public enum PropertyType
  {
    /// <summary>
    /// Project Tree Elements
    /// </summary>
    Project = 0,
    /// <summary>
    /// MT Elements
    /// </summary>
    Element =1
  }

 

 

function to find the correct Property (there are two different for us) 

 

 

/// <summary>
    /// Gets the correct property pane element.
    /// </summary>
    /// <param name="propertiesPanel">The properties panel.</param>
    /// <param name="type">The type.</param>
    /// <returns></returns>
    private static AutomationElement GetCorrectPropertyPaneElement(UIItem propertiesPanel, PropertyType type)
    {
      AutomationElement propertyWindowsElement = null;

      switch(type)
      {
        case PropertyType.Project:
        {
          propertyWindowsElement = propertiesPanel.GetElement(SearchCriteria.ByControlType(ControlType.Table).AndByText("Properties Window"));
          break;
        }
        case PropertyType.Element:
        {
          propertyWindowsElement = propertiesPanel.GetElement(SearchCriteria.ByAutomationId("MtXmlPropertyControl"));
          break;
        }
        default:
        {
          Assert.Fail( "Not the correct PropertyType: {0}", type.ToString() );
          break;
        }
      }
      Assert.IsNotNull(propertyWindowsElement);
      return propertyWindowsElement;
    }

 

A way to decide if the property window is a pane (docked) or  a single window

 

/// <summary>
    /// Finds the window element or open it with the given path using the menu.
    /// </summary>
    /// <param name="window">The window.</param>
    /// <param name="controlType">The <see cref="ControlType"/>.</param>
    /// <param name="name">The name of the element.</param>
    /// <param name="menuPathToOpen">The menu path to open.</param>
    /// <returns>The found or opened element or null if element can't be found.</returns>
    public static Object FindWindowElementOrOpen(UIItemContainer window, ControlType controlType, string name,string menuPathToOpen)
    {
      Object item;
      if( controlType == ControlType.Window )
      {
        item = StudioManagement.Application.GetWindow( name );
      }
      else
      {
        item = (UIItem)window.Get( SearchCriteria.ByControlType( controlType ).AndByText( name ) );
      }

      if (item == null)
      {
        StudioManagement.ClickMenuEntry(menuPathToOpen);
          if (controlType == ControlType.Window)
        {
          item = StudioManagement.Application.GetWindow(name);
        }
        else
        {
          item = window.Get(SearchCriteria.ByControlType(controlType).AndByText(name));
        }
        
      }
      return item;
    }

 

/// <summary>
    /// Finds the or open property window.
    /// </summary>
    /// <param name="testProjectWindow">The test project window.</param>
    /// <returns></returns>
    private static UIItem FindOrOpenPropertyWindow(UIItemContainer testProjectWindow)
    {
      UIItem propertiesPanel = (UIItem)UIItemElementHelper.FindWindowElementOrOpen(testProjectWindow, ControlType.Pane, "Properties", "View:Properties Window");
      if (propertiesPanel == null)
      {
        ClickMenuEntry("View");
        propertiesPanel = (UIItem)UIItemElementHelper.FindWindowElementOrOpen(testProjectWindow, ControlType.Pane, "Properties", "View:Other Windows:Properties Window");
      }
      return propertiesPanel;
    }

 

a class to handle a list of AutomationElements

 

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Automation;
using IEnumerable=System.Collections.IEnumerable;

namespace ManufacturerToolTest.Helper
{
  public class AutomationElementList : IEnumerable<AutomationElement>
  {
    /// <summary>
    /// Gets the <see cref="System.Windows.Automation.AutomationElement"/> at the specified index.
    /// </summary>
    /// <value></value>
    public AutomationElement this [int index]
    {
      get
      {
        if ( index >= _innerList.Count() )
        {
          throw new ArgumentOutOfRangeException();
        }
        return _innerList[index];
      }
    }
    List<AutomationElement> _innerList = new List<AutomationElement>();

    /// <summary>
    /// Initializes a new instance of the <see cref="AutomationElementList"/> class.
    /// </summary>
    /// <param name="source">The source.</param>
    public AutomationElementList(AutomationElementCollection source)
    {
      foreach (AutomationElement element in source)
      {
        _innerList.Add(element);
      }
    }

    /// <summary>
    /// Returns an enumerator that iterates through the collection.
    /// </summary>
    /// <returns>
    /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
    /// </returns>
    public IEnumerator<AutomationElement> GetEnumerator()
    {
      return _innerList.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return GetEnumerator();
    }
  }
}


No get the complete list of elements in the property window:

 /// <summary>
/// Gets the element list of properties window.
/// </summary>
/// <param name="testProjectWindow">The test project window.</param>
/// <param name="type">The type of the property window.</param>
/// <returns>a list with all elements in the property Window</returns>
public static AutomationElementList GetElementListOfPropertiesWindow( Window testProjectWindow, PropertyType type )
{

UIItem propertiesPanel = FindOrOpenPropertyWindow( testProjectWindow );

AutomationElement propertyWindowsElement = GetCorrectPropertyPaneElement( propertiesPanel, type );

AutomationElementList propertyWindowsColl = new AutomationElementList(propertyWindowsElement.FindAll(TreeScope.Subtree, Condition.TrueCondition));
return propertyWindowsColl;
}
testprocect window is the main window (Visual Studio) in this case.

now you can search on the elements in that list. Using "Where" from linq


AutomationElement element = propertyWindowsColl.Where(p-> p.Name == <the name of the element>). First();




to get or set the values of the elements the list , the ValuePattern of UiAutomation should be implemented.

/// <summary>
    /// Verifies the value and the readOnly state of the value pattern.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="value">The value.</param>
    /// <param name="isReadOnly">if set to <c>true</c> [is read only].</param>
    public static void VerifyValuePattern( AutomationElement element, string value, bool isReadOnly )
    {

      AutomationPattern automationPatternFromElement = GetSpecifiedPattern( element, "ValuePatternIdentifiers.Pattern" );

      if (automationPatternFromElement != null)
      {
        ValuePattern valuePattern = (ValuePattern)element.GetCurrentPattern(automationPatternFromElement);

        Assert.AreEqual(value.ToLower(), valuePattern.Current.Value.ToLower(), "Expected value: {0}, found value: {1} on element: {2}.", value, valuePattern.Current.Value, element.Current.Name);
        
        Assert.AreEqual( isReadOnly, valuePattern.Current.IsReadOnly, "Element: {0}.", element.Current.Name );

      }
      else
      {
        Assert.Fail("Value pattern could not be found");
      }

/// <summary>
    /// Sets the value pattern.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="value">The value.</param>
    public static void SetValuePattern(AutomationElement element, string value)
    {
      try
      {
        AutomationPattern automationPatternFromElement = GetSpecifiedPattern(element, "ValuePatternIdentifiers.Pattern");
        ValuePattern valuePattern = (ValuePattern)element.GetCurrentPattern(automationPatternFromElement);
        valuePattern.SetValue(value);
      }
      catch (Exception ex)
      {
        Assert.Fail("Exception: {0}. Try to set value: \"{1}\" on element; {2}", ex.Message, value, element.Current.Name);
      }
    }


Hope that helps.

Throndorin

 

 

Jul 22, 2009 at 2:18 PM

thanks again. I'll look through this and get back with you later.