Click here to Skip to main content
15,896,606 members
Articles / DevOps / Testing

Automation Enabler PlugIn - Exposes Inaccessible Elements to UI Automation Framework

Rate me:
Please Sign up or sign in to vote.
4.68/5 (7 votes)
20 Jun 2011CPOL7 min read 36.7K   921   24  
A reusable plug-in framework which exposes inaccessible elements to UI Automation framework. A must for .NET 2.0 WinForm container controls
using System;
using System.Collections.Generic;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows.Forms;
using System.Drawing;

namespace AutomationFramework
{
    /// <summary>
    /// Provider for the MeasurementItem in the MeasurementControl..
    /// </summary>
    public class ObjectProvider : FragmentProvider, IValueProvider
    {
        #region Constants
        private const int ourProviderOptionUseComThreading = 0x20;
        private const int ourProviderDescriptionId = 30107; // from MSDN 
        #endregion

        #region Fields
        private Control myControl;
        private Object myObject;
        private ApiMapping myApiMapping;
        #endregion

        #region Constructors
        public ObjectProvider(Control control, IRawElementProviderFragmentRoot root, Object value, ApiMapping apiMapping)
            : base(root, root) // (Parent, FragmentRoot)
        {
            this.myControl = control;
            this.myObject = value;
            this.myApiMapping = apiMapping;

            // Populate static properties            
            // TODO: Name should be localized.
            // Next Line Commented since we have overridden NameProperty.
            //AddAutomationProperty(AutomationElementIdentifiers.NameProperty.Id, this.myItem.Id.ToString());

            AddAutomationProperty(AutomationElementIdentifiers.ClassNameProperty.Id, myApiMapping.ControlClassName);
            AddAutomationProperty(AutomationElementIdentifiers.ControlTypeProperty.Id, ControlType.Custom.Id);
            // TODO: LocalizedControlType should be localized.
            AddAutomationProperty(AutomationElementIdentifiers.LocalizedControlTypeProperty.Id, myApiMapping.ObjectLocalizedControlTypeProperty);
            AddAutomationProperty(ourProviderDescriptionId, myApiMapping.ObjectProviderDescriptionId);
            AddAutomationProperty(AutomationElementIdentifiers.HelpTextProperty.Id, myApiMapping.ObjectHelpTextProperty);

            // The WinForm name for this control makes a good Automation ID.
            //AddAutomationProperty(AutomationElementIdentifiers.AutomationIdProperty.Id, this.myItem.Id.ToString());
            AddAutomationProperty(AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id, false);
            AddAutomationProperty(AutomationElementIdentifiers.IsControlElementProperty.Id, true);
            AddAutomationProperty(AutomationElementIdentifiers.IsContentElementProperty.Id, false);
            AddAutomationProperty(AutomationElementIdentifiers.IsEnabledProperty.Id, true);

            // Set Implemented Patterns
            if (myApiMapping.ObjectIValueProviderAvailable)
            {
                AddAutomationProperty(AutomationElementIdentifiers.IsValuePatternAvailableProperty.Id, true);
            }
        }
        #endregion

        #region Overridden Methods (Override base default behavior)
        public override ProviderOptions ProviderOptions
        {
            get
            {
                return (ProviderOptions)((int)ProviderOptions.ServerSideProvider | ourProviderOptionUseComThreading);
            }
        }

        public override object GetPatternProvider(int patternId)
        {
            // Return the patterns we have implemented.
            if (myApiMapping.ObjectIValueProviderAvailable && patternId == ValuePatternIdentifiers.Pattern.Id)
            {
                return this;
            }
            return base.GetPatternProvider(patternId);
        }

        /// <summary>
        /// Create a runtime ID.  Since there is only one fragment per item,
        /// the item turned into an integer is a unique identifier that we
        /// can use as the runtime ID.
        /// </summary>
        /// <returns></returns>
        public override int[] GetRuntimeId()
        {
            int[] runtimeId = new int[2];
            runtimeId[0] = AutomationInteropProvider.AppendRuntimeId;
            // Since we dont have any int id with MeasurementItem, set its Y Location. :)
            runtimeId[1] = this.myObject.GetHashCode();
            return runtimeId;
        }

        /// <summary>
        /// Get the Name of the Element.
        /// </summary>
        /// <returns></returns>
        protected override string GetName()
        {
            return this.myObject.GetType().GetProperty(myApiMapping.ObjectName).GetValue(myObject, null).ToString();
        }

        /// <summary>
        /// Get the bounding rect by consulting the control.
        /// </summary>
        public override System.Windows.Rect BoundingRectangle
        {
            get
            {
                // Bounding rects must be in screen coordinates
                object obj = (myObject.GetType().GetProperty(myApiMapping.ObjectBoundingRectangle).GetValue(myObject, null));
                Rectangle rect = (Rectangle)obj;
                System.Drawing.Rectangle screenRect = this.myControl.RectangleToScreen(rect);
                return new System.Windows.Rect(screenRect.Left, screenRect.Top, screenRect.Width, screenRect.Height);
            }
        }

        /// <summary>
        /// Return the fragment for the next value
        /// </summary>
        /// <returns></returns>
        protected override IRawElementProviderFragment GetNextSibling()
        {
            Object propertyValue = (myControl.GetType().GetProperty(myApiMapping.ObjectItems).GetValue(myControl, null));
            if (propertyValue != null && propertyValue.GetType().IsArray)
            {
                Array array = propertyValue as Array;

                int index = Array.IndexOf(array, myObject);
                if (index < array.Length - 1)
                {
                    index++;
                    object item = (object)array.GetValue(index);

                    return new ObjectProvider(this.myControl, this.fragmentRoot, item, myApiMapping);
                }
            }
            return null;
        }

        /// <summary>
        /// Return the fragment for the previous value
        /// </summary>
        /// <returns></returns>
        protected override IRawElementProviderFragment GetPreviousSibling()
        {
            Object propertyValue = (myControl.GetType().GetProperty(myApiMapping.ObjectItems).GetValue(myControl, null));
            if (propertyValue != null && propertyValue.GetType().IsArray)
            {
                Array array = propertyValue as Array;

                int index = Array.IndexOf(array, myObject);
                if (index > 0)
                {
                    index--;
                    object item = (object)array.GetValue(index);

                    return new ObjectProvider(this.myControl, this.fragmentRoot, item, myApiMapping);
                }
            }
            return null;
        }
        #endregion

        #region IValueProvider Members

        public bool IsReadOnly
        {
            get { return myApiMapping.ObjectIValueProviderIsReadOnly; }
        }

        public void SetValue(string value)
        {
            if (myApiMapping.ObjectIValueProviderAvailable)
            {
                myObject.GetType().GetProperty(myApiMapping.ObjectIValueProviderValue).SetValue(myObject, value, null);
            }
        }

        public string Value
        {
            get
            {
                string name = null; ;
                if (myApiMapping.ObjectIValueProviderAvailable)
                {
                    name = myObject.GetType().GetProperty(myApiMapping.ObjectIValueProviderValue).GetValue(myObject, null).ToString();
                }
                return name;
            }
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Philips
India India
Have been working with computers since the early 00's. Since then I've been building, fixing, configuring, installing, coding and designing with them. At present I mainly code windows applications in C#, WCF, WPF and SQL. I'm very interested in Design Patterns and try and use these generic principles in all new projects to create truly n-tier architectures. Also I like to code for making the User Interface very attractive...

Comments and Discussions